Hands-On RTOS with Microcontrollers - Brian Amos - E-Book

Hands-On RTOS with Microcontrollers E-Book

Brian Amos

0,0
39,59 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

A real-time operating system (RTOS) is used to develop systems that respond to events within strict timelines. Real-time embedded systems have applications in various industries, from automotive and aerospace through to laboratory test equipment and consumer electronics. These systems provide consistent and reliable timing and are designed to run without intervention for years.







This microcontrollers book starts by introducing you to the concept of RTOS and compares some other alternative methods for achieving real-time performance. Once you've understood the fundamentals, such as tasks, queues, mutexes, and semaphores, you'll learn what to look for when selecting a microcontroller and development environment. By working through examples that use an STM32F7 Nucleo board, the STM32CubeIDE, and SEGGER debug tools, including SEGGER J-Link, Ozone, and SystemView, you'll gain an understanding of preemptive scheduling policies and task communication. The book will then help you develop highly efficient low-level drivers and analyze their real-time performance and CPU utilization. Finally, you'll cover tips for troubleshooting and be able to take your new-found skills to the next level.







By the end of this book, you'll have built on your embedded system skills and will be able to create real-time systems using microcontrollers and FreeRTOS.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB

Seitenzahl: 573

Veröffentlichungsjahr: 2020

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Hands-On RTOS with Microcontrollers
Building real-time embedded systems using FreeRTOS, STM32 MCUs, and SEGGER debug tools
Brian Amos
BIRMINGHAM - MUMBAI

Hands-On RTOS with Microcontrollers

Copyright © 2020 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Commissioning Editor: Vijin BorichaAcquisition Editor:Shrilekha InaniContent Development Editor: Carlton BorgesSenior Editor: Rahul DsouzaTechnical Editor:Dinesh PawarCopy Editor:Safis EditingProject Coordinator: Neil DmelloProofreader: Safis EditingIndexer:Tejal Daruwale SoniProduction Designer: Joshua Misquitta

First published: May 2020

Production reference: 1150520

Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.

ISBN 978-1-83882-673-4

www.packt.com

Packt.com

Subscribe to our online digital library for full access to over 7,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

Why subscribe?

Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

Improve your learning with Skill Plans built especially for you

Get a free eBook or video every month

Fully searchable for easy access to vital information

Copy and paste, print, and bookmark content

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.

Contributors

About the author

Brian Amos is an embedded system engineer who has been programming with FreeRTOS since 2012. He is currently a senior firmware engineer in the telecom industry creating embedded systems used in ground stations for satellite communication. In the past, he led a team of engineers creating a flexible architecture to rapidly develop high-precision laboratory test equipment. Prior to this, he worked with early mesh networked energy harvesting sensors used to help predict when industrial machinery needed maintenance.

About the reviewer

Phillip Johnston is a principal at Embedded Artistry, an embedded systems firm focused on improving early-stage hardware product development and educating developers and engineers around the world. Embedded Artistry focuses on building a solid foundation for building systems through systems architecture, modular firmware development, and automated software quality processes. His experience spans consumer electronics, defense, automotive, robotics, cameras, drones, and manufacturing.

Packt is searching for authors like you

If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.

Table of Contents

Title Page

Copyright and Credits

Hands-On RTOS with Microcontrollers

About Packt

Why subscribe?

Contributors

About the author

About the reviewer

Packt is searching for authors like you

Preface

Who this book is for

What this book covers

To get the most out of this book

Download the example code files

Download the color images

Conventions used

Get in touch

Reviews

Section 1: Introduction and RTOS Concepts

Introducing Real-Time Systems

Technical requirements

What is real-time anyway?

The ranges of timing requirements

The ways of guaranteeing real-time behavior

Types of real-time systems

Hardware

Bare-metal firmware

RTOS-based firmware

RTOS-based software

Carefully crafted OS software

Defining RTOS

Hard real-time systems

Firm real-time systems

Soft real-time systems

The range of RTOSes

The RTOS used in this book

Deciding when to use an RTOS

Summary

Questions

Understanding RTOS Tasks

Technical requirements

Introducing super loop programming

The basic super loop

Super loops in real-time systems

Achieving parallel operations with super loops

Introducing interrupts

Interrupts and super loops

Introducing DMA

Scaling a super loop

Comparing RTOS tasks to super loops

Achieving parallel operations with RTOS tasks

Theoretical task programming model

Round-robin scheduling

Preemptive-based scheduling

RTOS tasks versus super loops – pros and cons

Summary

Questions

Further reading

Task Signaling and Communication Mechanisms

Technical requirements

RTOS queues

Simple queue send

Simple queue receive

Full queue send

Empty queue receive

Queues for inter-task communication

RTOS semaphores

Counting semaphores

Binary semaphores

RTOS mutexes

Priority inversion

Mutexes minimize priority inversion

Summary

Questions

Section 2: Toolchain Setup

Selecting the Right MCU

Technical requirements

The importance of MCU selection

MCU considerations

Core considerations

Physical size

ROM

RAM

The CPU clock rate

Interrupt processing

Price

Availability

Hardware peripherals

Connectivity

Memory protection units

Hardware floating-point units

Digital signal processing functions

Direct memory access channels

Communication interfaces

Hardware crypto engines

Timing hardware

Integrated analog

Dedicated touch interfaces

Display interfaces

External memory support

Real-time clock

Audio support

Power consumption

Power efficiency

Low-power modes

Wake-up time

Power supply voltage

Migrating MCUs mid-project

Importance of pin compatibility

Peripheral similarity

The concept of an MCU family

Development board considerations

What a development platform is and why it matters

Evaluation kits

Low-cost demonstration boards

Introducing the STM32 product line

Mainstream

High performance

The heterogeneous multi-core approach

Low power

Wireless

How our development board was selected

Requirements

Requirements justification

Choosing the dev board

Summary

Questions

Further reading

Selecting an IDE

Technical requirements

The IDE selection criteria

Free MCU vendor IDEs and hardware-centric IDEs

STM32CubeIDE

Platform-abstracted IDEs

ARM Mbed Studio

Arduino IDE

Open source/free IDEs

AC6 System Workbench for STM32 (S4STM32)

Eclipse CDT and GCC

Microsoft Visual Studio Code

Proprietary IDEs

ARM/Keil uVision

IAR Embedded Workbench

Rowley CrossWorks

SEGGER Embedded Studio

SysProgs Visual GDB

Selecting the IDE used in this book

Considering STM32Cube

Device selection

Hardware bring-up

Middleware setup

Code generation trade-offs

Setting up our IDE

Installing STM32CubeIDE

Importing the source tree into STM32CubeIDE

Summary

Questions

Further reading

Debugging Tools for Real-Time Systems

Technical requirements

The importance of excellent debugging tools

RTOS-aware debugging

RTOS visualization

Using SEGGER J-Link

Hardware options

Segger J-Trace

SEGGER J-Link

SEGGER J-Link on-board

Installing J-Link

Converting ST-Link to J-Link

Using SEGGER Ozone

File types used in the examples

Installing SEGGER Ozone

Creating Ozone projects

Attaching Ozone to the MCU

Viewing tasks

Task-based stack analysis

Using SEGGER SystemView

Installing SystemView

SystemView installation

Source code configuration

Using SystemView

Other great tools

Test-driven development

Static analysis

Percepio Tracealyzer

Traditional testing equipment

Summary

Questions

Further reading

Section 3: RTOS Application Examples

The FreeRTOS Scheduler

Technical requirements

Creating tasks and starting the scheduler

Hardware initialization

Defining task functions

Creating tasks

Checking the return value

Starting the scheduler

Deleting tasks

The task deletes itself

Deleting a task from another task

Trying out the code

Task memory allocation

Heap allocated tasks

Statically allocated tasks

Memory protected task creation

Task creation roundup

Understanding FreeRTOS task states

Understanding different task states

Running

Ready

Blocked

Suspended

Optimizing task states

Optimizing to reduce CPU time

Optimizing to increase performance

Optimizing to minimize power consumption

Troubleshooting startup problems

None of my tasks are running!

Task creation failed

Scheduler returns unexpectedly

Important notes

Summary

Questions

Further reading

Protecting Data and Synchronizing Tasks

Technical requirements

Using semaphores

Synchronization via semaphores

Setting up the code

Understanding the behavior

Wasting cycles – synchronization by polling

Setting up the code

Understanding the behavior

Time-bound semaphores

Setting up the code

Understanding the behavior

Counting semaphores

Priority inversion (how not to use semaphores)

Setting up the code

Task A (highest priority)

Task B (medium priority)

Task C (low priority)

Understanding the behavior

Using mutexes

Fixing priority inversion

Setting up the code

Understanding the behavior

Avoiding mutex acquisition failure

Avoiding race conditions

Failed shared resource example

Using software timers

Setting up the code

Oneshot timers

Repeat timers

Understanding the behavior

Software timer guidelines

Example use cases

Considerations

Limitations

Summary

Questions

Further reading

Intertask Communication

Technical requirements

Passing data through queues by value

Passing one byte by value

Passing a composite data type by value

Understanding how queues affect execution

Important notes on the examples

Passing data through queues by reference

When to pass by reference

Important notes

Direct task notifications

Passing simple data using task notifications

Other options for task notifications

Comparing direct task notifications to queues

Summary

Questions

Further reading

Section 4: Advanced RTOS Techniques

Drivers and ISRs

Technical requirements

Introducing the UART

Setting up the UART

Creating a polled UART driver

Analyzing the performance

Pros and cons of a polled driver

Usage of polled drivers

Differentiating between tasks and ISRs

Using the FreeRTOS API from interrupts

Creating ISR-based drivers

Queue-based driver

uartPrintOutTask

startReceiveInt

USART2_IRQHandler

Tips for linking ISRs

startUart4Traffic

Performance analysis

A buffer-based driver

startReceiveInt

uartPrintOutTask

USART2_IRQHandler

startUart4Traffic

Performance analysis

Creating DMA-based drivers

Configuring DMA peripherals

A buffer-based driver with DMA

Performance analysis

Stream buffers (FreeRTOS 10+)

Using the stream buffer API

Setting up double-buffered DMA

Populating the stream buffer

Improving the stream buffer

Analyzing the performance

Choosing a driver model

How is the calling code designed?

How much delay is acceptable?

How fast is data moving?

What type of device are you interfacing?

When to use queue-based drivers

When to use buffer-based drivers

When to use stream buffers

Using third-party libraries (STM HAL)

Summary

Questions

Further reading

Sharing Hardware Peripherals across Tasks

Technical requirements

Understanding shared peripherals

Defining the peripheral driver

Introducing the STM USB driver stack

Using the stock CDC drivers

Developing a StreamBuffer USB virtual COM port

Public functions

Private functions

Putting it all together

Using mutexes for access control

Extending VirtualCommDriver

Guaranteeing atomic transactions

Summary

Questions

Tips for Creating a Well-Abstracted Architecture

Technical requirements

Understanding abstraction

Grasping an abstraction is fast

An example with abstraction

An example without abstraction

Abstractions provide flexibility

Why abstraction is important

Recognizing opportunities to reuse code

Avoiding the copy-paste-modify trap

Writing reusable code

Writing reusable drivers

Developing an LED interface

Reusing code containing tasks

Testing flexible code

Organizing source code

Choosing locations for source files

Dealing with changes

Summary

Questions

Further reading

Creating Loose Coupling with Queues

Technical requirements

Understanding queues as interfaces

Queues make excellent interface definitions

Queues increase flexibility

Queues make testing easier

Creating a command queue

Deciding on queue contents

Defining the architecture

ledCmdExecutor

Frame decoding

The USB virtual comm driver

Using the code

Reusing a queue definition for a new target

The queue interface

The iPWM interface

Summary

Questions

Choosing an RTOS API

Technical requirements

Understanding generic RTOS APIs

Advantages of generic APIs

Disadvantages of generic APIs

Comparing FreeRTOS and CMSIS-RTOS

Considerations during migration

Cross-referencing CMIS-RTOS and FreeRTOS functions

Delay functions

EventFlags

Kernel control and information

Message queues

Mutexes and semaphores

Semaphores

Thread flags

Thread control/information

Timers

Memory pools

Creating a simple CMSIS-RTOS v2 application

FreeRTOS and POSIX

Creating a simple FreeRTOS POSIX application

Pros and cons to using the POSIX API

Deciding which API to use

When to use the native FreeRTOS API

When to use the CMSIS-RTOS API

When to use the POSIX API

Summary

Questions

Further reading

FreeRTOS Memory Management

Technical requirements

Understanding memory allocation

Static memory

Stack memory

Heap memory

Heap fragmentation

Static and dynamic allocation of FreeRTOS primitives

Dynamic allocation examples

Creating a task

Creating a queue

Static allocation examples

Creating a task

Creating a queue

Eliminating all dynamic allocation

Comparing FreeRTOS heap implementations

Choosing your RTOS heap implementation

Replacing malloc and free

Implementing FreeRTOS memory hooks

Keeping an eye on stack space

Keeping an eye on heap space

Using a memory protection unit (MPU)

Summary

Questions

Further reading

Multi-Processor and Multi-Core Systems

Technical requirements

Introducing multi-core and multi-processor systems

Exploring multi-core systems

Heterogeneous multi-core systems

Inter-core communication

Legacy application extension

High-demand hard real-time systems

Homogeneous multi-core systems

Exploring multi-processor systems

Distributed systems

Parallel development

Design reuse

High-reliability systems

Exploring inter-processor communication

Choosing the right communication medium

Communication standards

Controller area network

Ethernet

Inter-integrated communication bus

Local interconnect network

Modbus

Serial peripheral interface

USB as an inter-processor communication bus

Choosing between multi-core and multi-processor systems

When to use multi-core MCUs

When to use multi-processor systems

Summary

Questions

Further reading

Troubleshooting Tips and Next Steps

Technical requirements

Useful tips

Using tools to analyze threads

Keeping an eye on memory usage

Stack overflow checking

Fixing SystemView dropped data

Using assertions

configAssert

Debugging a hung system with configAssert()

Collecting the data

Digging deeper – SystemView data breakpoints

Next steps

Summary

Questions

Assessments

Chapter 1

Chapter 2

Chapter 3

Chapter 4

Chapter 5

Chapter 6

Chapter 7

Chapter 8

Chapter 9

Chapter 10

Chapter 11

Chapter 12

Chapter 13

Chapter 14

Chapter 15

Chapter 16

Chapter 17

Other Books You May Enjoy

Leave a review - let other readers know what you think

Preface

This hands-on guide will provide you with the most important functional knowledge for getting a Real-Time Operating System (RTOS) up and running on a microcontrollerunit (MCU). If you're interested in learning how to implement applications using an RTOS with hands-on examples using actual hardware and discussing common performance versus development-time trade-offs, you're in the right place!

We'll be implementing code using the FreeRTOS kernel, working with the popular STM32 ARM MCUs using a low-cost STM Nucleo development board, and debugging/analyzing code with SEGGER debug tools. All of the tools used in this book have been selected because they are easily accessible for the hobbyist or professional just getting started, and also because of their popularity in real-world professional teams. The knowledge and experience you gain through reading this book and working through the examples will be directly applicable to actual development in a professional environment.

Who this book is for

RTOS programming is not a beginner's topic and is definitely not the right starting point for learning about embedded systems. If MCUs or the C language is totally new to you, then you're better off starting by covering the basics and getting some hands-on experience before diving into this more advanced topic.

So, who stands to benefit the most from working through this book?

Professional programmers: You've always programmed on bare metal (no OS) and are looking to increase your MCU programming skills by learning how to use an RTOS to meet tight timing requirements, balance concurrent operations, and create modular code.

Students interested in "getting their hands dirty": You've been covering theory, listening to lectures, and coding lab exercises, but now you're looking for a complete guide that helps you to get started with something you can physically touch and interact with.

Makers moving onto more advanced topics: You've written some sketches or scripts, but you're looking for your next challenge. Maybe you'd like to create a full MCU-based system from scratch – the information here will help get you on track for the programming side. You'll even get some tips on what to look for when selecting an MCU for your project.

What this book covers

This book comprises 17 chapters in all, spread across four sections. It isn't necessary to read the book straight through if you're already comfortable with some of the material. For example, if you're already comfortable with basic RTOS concepts and real-time systems, feel free to skip to Chapter 4, Selecting the Right MCU. The following are brief descriptions of the chapters that this book is made up of:

Chapter 1, Introducing Real-Time Systems, is a simple introduction to what an RTOS is and when and why to use one. Hardware and software alternatives to an MCU-based RTOS are also discussed.

Chapter 2, Understanding RTOS Tasks, provides a comparison of super loops with RTOS tasks, including various ways parallel operations can be achieved using both.

Chapter 3, Task Signaling and Communication Mechanisms, is a short introduction to more RTOS concepts with lots of diagrams. This chapter, along with Chapter 2, Understanding RTOS Tasks, should be useful as a reference and a quick refresher on theconcepts and terminology, should you ever need it.

Chapter 4, Selecting the Right MCU, helps you understand what considerations should be made when selecting an MCU. After gaining an appreciation of the inter-dependency between hardware and firmware, we look at why it is so important that hardware and firmware engineers both have a hand in system design.

Chapter 5, Selecting an IDE, introduces and discusses various types of Integrated Development Environments (IDEs), including reasons why you might decide to choose one over another (or none at all). Instructions on setting up STM32CubeIDE and importing the example code are covered here.

Chapter 6, Debugging Tools for Real-Time Systems, covers tools for debugging embedded systems, including the debugging tool we'll be using throughout the remainder of the book – SEGGER Ozone and SEGGER SystemView visualization software. Instructions on how to use Ozone and SystemView are covered here. Hardware-based test equipment and some other useful tools for your embedded system development workflow are also included.

Chapter 7, The FreeRTOS Scheduler, teaches you the various ways to create tasks using FreeRTOS and how to troubleshoot startup failures. You will gain an understanding of task states and the different ways performance can be optimized.

Chapter 8, Protecting Data and Synchronizing Tasks, covers task synchronization using semaphores and data protection using mutexes, as well as how to avoid race conditions and priority inversion. Software timers are also covered.

Chapter 9, Intertask Communication, examines different ways of passing information between tasks, with different examples of using queues for passing information by value and reference, discussing the advantages and considerations of both approaches. We'll also learn about a lightweight intertask communication mechanism, the direct task notification, including a comparison of task notifications and queues.

Chapter 10, Drivers and ISRs, dives deep into several detailed examples of how to implement efficient drivers with various FreeRTOS primitives including semaphores, queues, and stream buffers. We'll also look at how FreeRTOS can be used in conjunction with MCU hardware such as DMA to provide extremely CPU-efficient driver implementations. This chapter works both directly with the MCUs peripheral registers and also with STM32 HAL code.

Chapter 11, Sharing Hardware Peripherals across Tasks, teaches you how to create drivers that can be safely used across multiple tasks while sharing hardware resources. We'll adapt the STM-supplied USB CDC implementation to be more user-friendly and efficient, wrapping it with a mutex and queues so it is safe to use across multiple tasks.

Chapter 12, Tips on Creating a Well-Abstracted Architecture, covers code reusability, flexibility, and hardware portability, with an eye on creating abstractions that make your job easier. Some suggestions for source code organization to help facilitate reuse are also covered.

Chapter 13, Creating Loose Coupling with Queues, is a culmination of all of the concepts covered in the book. It includes a fully fleshed-out example of a loosely coupled architecture used to create a properly abstracted, end-to-end application. We'll use the USB CDC virtual comm port developed earlier, as well as an LED abstraction, to create a loosely coupled, fully reusable LED sequencer using a command queue. This embedded application can be controlled from a PC with a cross-platform UI written in Python.

Chapter 14, Choosing an RTOS API, continues our high-level architecture discussion with a look at three different APIs available to use for accessing FreeRTOS functionality: the native FreeRTOS API, ARM's CMSIS-RTOS, and POSIX. Discussion topics include a comparison of the available features and why you might choose one of the others for different projects.

Chapter 15, FreeRTOS Memory Management, takes a close look at a few different options for memory management in FreeRTOS. We'll look at static versus dynamic allocation, as well as using a Memory Protection Unit (MPU).

Chapter 16, Multi-Processor and Multi-Core Systems, teaches you how multi-processor and multi-core systems are used for a variety of reasons – learn what they are and how to get the different parts of a system to communicate.

Chapter 17, Troubleshooting Tips and Next Steps, covers tips for troubleshooting systems, including tips on how to avoid stack overflows and how to troubleshoot a hung system. Some recommendations for the next steps are also covered.

To get the most out of this book

Every effort has been made to make working through the examples in this book as easy as possible for a very wide range of people. To get the most out of the book (by working through the examples), you'll need the following hardware:

A Windows, macOS, or Linux PC with internet access

An STM32

Nucleo-F767ZI

development board

Two Micro-USB cables

Jumper wires—20 to 22 AWG (~0.65 mm) solid core wire

Detailed setup instructions for the different tools used are included in the chapters.

If you are using the digital version of this book, we advise you to type the code yourself or access the code via the GitHub repository (link available in the next section). Doing so will help you avoid any potential errors related tothe copying and pasting of code.

Since this book targets programming low-level embedded systems, we'll be using C as the language of choice. Some knowledge of microcontrollers is assumed, as is the ability to read a datasheet. If you have a good understanding of the C language (or C++), then you should be comfortable reading this book – no previous RTOS knowledge is required. Since we'll be working with MCUs in an embedded system, there will be some occasional discussions on the hardware side as well, primarily dealing with features of MCUs and development boards. These topics will be covered in enough detail that someone with minimal hardware knowledge should be able to follow without too much difficulty. You should be comfortable interacting with and handling development hardware, although there isn't any actual assembly required.

Download the example code files

You can download the example code files for this book from your account athttps://github.com/PacktPublishing/Hands-On-RTOS-with-Microcontrollers. If you purchased this book elsewhere, you can visitwww.packtpub.com/supportand register to have the files emailed directly to you.

You can download the code files by following these steps:

Log in or register at

www.packt.com

.

Select the

Support

tab.

Click on

Code Downloads

.

Enter the name of the book in the

Search

box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

WinRAR/7-Zip for Windows

Zipeg/iZip/UnRarX for Mac

7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on GitHub athttps://github.com/PacktPublishing/Hands-On-RTOS-with-Microcontrollers. In case there's an update to the code, it will be updated on the existing GitHub repository.

We also have other code bundles from our rich catalog of books and videos available athttps://github.com/PacktPublishing/. Check them out!

Download the color images

We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here:http://www.packtpub.com/sites/default/files/downloads/9781838826734_ColorImages.pdf.

Get in touch

Feedback from our readers is always welcome.

General feedback: If you have questions about any aspect of this book,mention the book title in the subject of your message and email us [email protected].

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visitwww.packtpub.com/support/errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us [email protected] a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visitauthors.packtpub.com.

Reviews

Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

For more information about Packt, please visit packt.com.

Section 1: Introduction and RTOS Concepts

What is a real-time system and what are the major components that make up a real-time operating system (RTOS)? These are the questions we'll be answering in the first section of this book. This prerequisite knowledge will serve as a foundation we'll build upon with working examples and hands-on exercises in later chapters. If you're already familiar with another RTOS, you can probably skim or skip this section.

This section comprises the following chapters:

Chapter 1

,

Introducing Real-Time Systems

Chapter 2

,

Understanding RTOS Tasks

Chapter 3

,

Task Signaling and Communication Mechanisms

Introducing Real-Time Systems

Real-time systems come in a wide variety of implementations and use cases. This book focuses on how to use a real-time OS (RTOS) to create real-time applications on a microcontroller unit (MCU).

In this chapter, we'll start with an overview of what an RTOS is and get an idea of the wide range of systems that can have real-time requirements. From there, we'll look at some of the different ways of achieving real-time performance, along with an overview of the types of systems (such as hardware, firmware, and software) that may be used. We'll wrap up by discussing when it is advisable to use an RTOS in an MCU application and when it might not be necessary at all.

In a nutshell, we will cover the following topics in this chapter:

What is "real-time" anyway?

Defining RTOS

Deciding when to use an RTOS

Technical requirements

There are no software or hardware requirements for this chapter.

What is real-time anyway?

Any system that has a deterministic response to a given event can be considered "real-time." If a system is considered to fail when it doesn't meet a timing requirement, it must be real-time. How failure is defined (and the consequences of a failed system) can vary widely. It is extremely important to realize that real-time requirements can vary widely, both in the speed of the timing requirement and also the severity of consequences if the required real-time deadlines are not met.

The ranges of timing requirements

To illustrate the range of timing requirements that can be encountered, let's consider a few different systems that acquire readings from analog-to-digital converters (ADCs).

The first system we'll look at is a control system that is set up to control the temperature of a soldering iron (as seen in the following diagram). The parts of the system we're concerned with are the MCU, ADC, sensor, and heater.

The MCU is responsible for the following:

Taking readings from a temperature sensor via the ADC

Running a closed-loop control algorithm (to maintain a constant temperature at the soldering iron tip)

Adjusting the output of the heater as needed

These can be seen in the following diagram:

Since the temperature of the tip doesn't change incredibly quickly, the MCU may only need to acquire 50 ADC samples per second (50 Hz). The control algorithm responsible for adjusting the heater (to maintain a constant temperature) runs at an even slower pace, 5 Hz:

The ADC will assert a hardware line, signaling a conversion has been completed and is ready for the MCU to transfer the reading to its internal memory. The MCU reading the ADC has up to 20 ms to transfer data from the ADC to internal memory before a new reading needs to be taken (as seen in the following diagram). The MCU also needs to be running the control algorithm to calculate the updated values for the heater output at 5 Hz (200 ms). Both of these cases (although not particularly fast) are examples of real-time requirements:

Now, on the other end of the ADC reading spectrum, we could have a high bandwidth network analyzer or oscilloscope that is going to be reading an ADC at a rate of tens of GHz! The raw ADC readings will likely be converted into the frequency domain and graphically displayed on a high-resolution front panel dozens of times a second. A system like this requires huge amounts of processing to be performed and must adhere to extremely tight timing requirements, if it is to function properly.

Somewhere in the middle of the spectrum, you'll find systems such as closed-loop motion controllers, which will typically need to execute their PID control loops between hundreds of Hz to tens of kHz in order to provide stability in a fast-moving system. So, how fast is real-time? Well, as you can see from the ADC examples alone, it depends.

In some of the previous cases, such as the oscilloscope or soldering iron, failure to meet a timing requirement results in poor performance or incorrect data being reported. In the case of the soldering iron, this might be poor temperature control (which could cause damage to components). For the test equipment, missing deadlines could cause erroneous readings, which is a failure. This may not seem like a big deal to some people, but for the users of that equipment, who are relying on the accuracy of the data being reported, it is likely to matter a great deal. Some laboratory equipment that is used in standard verification provides checks for product conformance. If there is an undetected malfunction in the equipment that results in an inaccurate measurement, an incorrect value could be reported. It may be possible for a suspect test to be rerun. Eventually, however, if retesting is required too often and reliable readings can't be counted on, then the test equipment will start to become suspect and viewed as unreliable and sales will decline—all because a real-time requirement wasn't being consistently met.

In other systems, such as the flight control of a UAV or motion control in industrial process control, failing to run the control algorithm in a timely manner could result in something more physically catastrophic, such as a crash. In this case, the consequences are potentially life-threatening.

Thankfully, there are steps that can be taken to avoid all of these failure scenarios.

The ways of guaranteeing real-time behavior

One of the easiest ways to ensure a system does what it is meant to do is to make sure it is as simple as possible while still meeting the requirements. This means resisting the urge to over-complicate a simple task. If a toaster is meant to toast a slice of bread, don't put a display on it and make it tell you the weather too; just have it turn on a heating element for the right amount of time. This simple task has been accomplished for years without requiring any code or programmable devices whatsoever.

As programmers, if we come across a problem, we have a tendency to immediately reach for the nearest MCU and start coding. However, some functions of a product (especially true if a product has electro-mechanical components) are best handled without code at all. A car window doesn't really need an MCU with a polling loop to run, turning on motors through drivers and watching sensors for feedback to shut them off. This task can actually be handled by a few mechanical switches and diodes. If a feedback-reporting mechanism is required for a given system—such as an error that needs to be asserted in the case of a stuck window—then there may be no choice but to use a more complex solution. However, our goal as engineers should always be the same—solve the problem as simply as possible, without adding additional complexity.

If a problem can be solved by hardware alone, then explore that possibility with the team first, before breaking out the MCU. If a problem can be handled by using a simple while loop to perform some polling of the sensor status, then simply poll the sensor for the status; there may be no need to start coding interrupt service routines (ISRs). If the functionality of the device is single-purposed, there are many cases where a full-blown RTOS can simply get in the way—so don't use one!

Types of real-time systems

There are many different ways of achieving real-time behavior. The following section is a discussion on the various types of real-time systems you might encounter. Also note that it is possible to have combinations of the following systems working together as subsystems. These different subsystems can occur at a product, board, or even chip level (this approach is discussed in Chapter 16, Multi-Processor and Multi-Core Systems).

Hardware

The original real-time system, hardware, is still the go-to for extremely tight tolerance and/or fast timing requirements. It can be implemented with discrete digital logic, analog components, programmable logic, or an application-specific integrated component (ASIC). Programmable logic devices (PLDs), complex programmable logic devices (CPLDs), and field-programmable gate arrays (FPGAs) are the various members of the programmable logic device portion of this solution. Hardware-based real-time systems can cover anything from analog filters, closed loop control, and simple state machines to complex video codecs. When implemented with power saving in mind, ASICs can be made to consume less power than an MCU-based solution. In general, hardware has the advantage of performing operations in parallel and instantly (this is, of course, an over-simplification), as opposed to a single-core MCU, which only gives the illusion of parallel processing.

The downsides for real-time hardware development generally include the following:

The inflexibility of non-programmable devices.

The expertise required is generally less commonly available than software/firmware developers.

The cost of full-featured programmable devices (for example, large FPGAs).

The high cost of developing a custom ASIC.

Bare-metal firmware

Bare-metal firmware is considered (for our purposes) to be any firmware that isn't built on top of a preexisting kernel/scheduler of some type. Some engineers take this a step further, arguing that true bare-metal firmware can't use any preexisting libraries (such as vendor supply hardware abstraction libraries)—there is some merit to this view as well. A bare-metal implementation has the advantage that the user's code has total control of all aspects of the hardware. The only way for the main loop code execution to be interrupted is if an interrupt fires. In this case, the only way for anything else to take control of the CPU is for the existing ISR to finish or for another higher-priority interrupt to fire.

Bare-metal firmware solutions excel when there is a small number of relatively simple tasks to perform—or one monolithic task. If the firmware is kept focused and best practices are followed, deterministic performance is generally easy to measure and guarantee due to the relatively small number of interactions between ISRs (or in some cases, a lack of ISRs). In some extreme cases for heavily loaded MCUs (or MCUs that are highly constrained in ROM/RAM), bare-metal is the only option.

As bare-metal implementations get to be more elaborate when dealing with events asynchronously, they start to overlap with functionality provided by an RTOS. An important consideration to keep in mind is that by using an RTOS—rather than attempting to roll your own thread-safe system—you automatically benefit from all of the testing the RTOS provider has put in. You'll also have the opportunity to use code that has the power of hindsight behind it—all of the RTOSes available today have been around for several years. The authors have been adapting and adding functionality the entire time to make them robust and flexible for different applications.

RTOS-based firmware

Firmware that runs a scheduling kernel on an MCU is RTOS-based firmware. The introduction of the scheduler and some RTOS-primitives allows tasks to operate under the illusion they have the processor to themselves (discussed in detail in Chapter 2, Understanding RTOS Tasks). Using an RTOS enables the system to remain responsive to the most important events while performing other complex tasks in the background.

There are a few downsides to all of these tasks running. Inter-dependencies can arise between tasks sharing data—if not handled properly, the dependency will cause a task to block unexpectedly. Although there are provisions for handling this, it does add complexity to the code. Interrupts will generally use task signaling to take care of the interrupt as quickly as possible and defer as much processing to a task as possible. If handled properly, this solution is excellent for keeping complex systems responsive, despite many complex interactions. However, if handled improperly, this design paradigm can lead to more timing jitter and less determinism.

RTOS-based software

Software running on a full OS that contains a memory management unit (MMU) and central processing unit (CPU) is considered RTOS-based software. Applications that are implemented with this approach can be highly complex, requiring many different interactions between various internal and external systems. The advantage of using a full OS is all of the capability that comes along with it—both hardware and software.

On the hardware side, there are generally more CPU cores available running at higher clock rates. There can be gigabytes of RAM and persistent memory available. Adding peripheral hardware can be as simple as the addition of a card (provided there are pre-existing drivers).

On the software side, there is a plethora of open source and vendor proprietary solutions for networking stacks, UI development, file handling, and so on. Underneath all of this capability and options, the kernel is still implemented in such a way that the critical tasks won't be blocked for an indefinite period of time, which is possible with a traditional OS. Because of this, getting deterministic performance is still within reach, just like with RTOS firmware.

Carefully crafted OS software

Similar to RTOS-based software, a standard OS has all of the libraries and features a developer could ask for. What's missing, however, is a strict focus on meeting timing requirements. Generally speaking, systems implemented with a traditional OS are going to have much less deterministic behavior (and none that can be truly counted on in a safety-critical situation). If there is a lax real-time requirement without catastrophic consequences, if a wishy-washy deadline isn't met on time, a standard OS can be made to work, as long as care is taken in choosing what software stacks are running and their resource use is kept in check. The Linux kernel with PREEMPT_RT patches is a good example of this type of real-time system.

So, now that all of the options for achieving a real-time system have been laid out, it's time to define exactly what we mean when we say RTOS, specifically an MCU-based RTOS.

Defining RTOS

OSes (such as Windows, Linux, and macOS) were created as a way to provide a consistent programming environment that abstracted away the underlying hardware to make it easier to write and maintain computer programs. They provide the application programmer with many different primitives (such as threads and mutexes) that can be used to create more complex behavior. For example, it is possible to create a multi-threaded program that provides protected access to shared data:

The preceding application doesn't implementthread and mutex primitives, it only makes use of them. The actual implementations of threads and mutexes are handled by the OS. This has a few advantages:

The application code is less complex.

It is easier to understand—the same primitives are used regardless of the programmer, making it easier to understand code created by different people.

The is better hardware

portability—

with the proper precautions, the code can be run on any hardware supported by the OS without modification.

In the preceding example, a mutex is used to ensure that only one thread can access the shared data at a time. In the case of a general-purpose OS, each thread will happily wait for the mutex to become available indefinitely before moving on to access the shared data. This is where RTOSes diverge from general-purpose OSes. In an RTOS, all blocking system calls are time-bound. Instead of waiting for the mutex indefinitely, an RTOS allows a maximum delay to be specified. For example, if Thread 1 attempts to acquire Mutex and still doesn't have it after 100 ms, or 1 second, it will continue waiting for the mutex to become available.

In an RTOS implementation, the maximum amount of time to wait for Mutex to become available is specified. If Thread 1 specifies that it must acquire the mutex within 100 ms and still hasn't received the mutex after 101 ms, Thread 1 will receive a notification that the mutex hasn't been acquired in time. This timeout is specified to help create a deterministic system.

Any OS that provides a deterministic way of executing a given piece of code can be considered a real-time OS. This definition of RTOS covers a fairly large number of systems.

There are a couple of characteristics that tend to differentiate one RTOS application from another: how often not meeting a real-time deadline is acceptable and the severity of not meeting a real-time deadline. The different ranges of RTOS applications are usually lumped into three categories—hard, firm, and soft real-time systems.

Don't get too hung up on the differences between firm and soft real-time systems. The definitions for these terms don't even have unanimous agreement from within our industry. What does matter is that you know your system's requirements and design a solution to meet them!

The severity of a failure is generally deemed safety-critical if a failure will cause the loss of life or significant property. There are hard real-time systems that have nothing to do with safety.

Hard real-time systems

A hard real-time system must meet its deadline 100% of the time. If the system does not meet a deadline, then it is considered to have failed. This doesn't necessarily mean a failure will hurt someone if it occurs in a hard real-time system—only that the system has failed if it misses a single deadline.

Some examples of hard real-time systems can be found in medical devices, such as pacemakers and control systems with extremely tightly controlled parameters. In the case of a pacemaker, if the pacemaker misses a deadline to administer an electrical pulse at the right moment in time, it might kill the patient (this is why pacemakers are defined as safety-critical systems).

In contrast, if a motion control system on a computer numerical control (CNC) milling machine doesn't react to a command in time, it might plunge a tool into the wrong part of the part being machined, ruining it. In these cases that we have mentioned, one failure caused a loss of life, while the other turned some metal into scrap—but both were failures caused by a single missed deadline.

Firm real-time systems

As opposed to hard real-time systems, firm real-time systems need to hit their deadlines nearly all of the time. If video and audio lose synchronization momentarily, it probably won't be considered a system failure, but will likely upset the consumer of the video.

In most control systems (similar to the soldering iron in a previous example), a few samples that are read slightly outside of their specified time are unlikely to completely destroy system control. If a control system has an ADC that automatically takes a new sample, if the MCU doesn't read the new sample in time, it will be overwritten by a new one. This can occur occasionally, but if it happens too often or too frequently, the temperature stability will be ruined. In a particularly demanding system, it may only take a few missed samples before the entire control system is out of spec.

Soft real-time systems

Soft real-time systems are the most lax when it comes to how often the system must meet its deadlines. These systems often offer only a best-effort promise for keeping deadlines.

Cruise control in a car is a good example of a soft real-time system because there are no hard specifications or expectations of it. Drivers typically don't expect their speed to converge to within +/- x mph/kph of the set speed. They expect that given reasonable circumstances, such as no large hills, the control system will eventually get them close to their desired speed most of the time.

The range of RTOSes

RTOSes range in their functionality, as well as the architecture and size of the processor they're best suited to. On the smaller side, we have smaller 8–32-bit MCU-focused RTOSes, such as FreeRTOS, Keil RTX, MicriumµC, ThreadX, and many more. This class of RTOS is suitable for use on microcontrollers and provides a compact real-time kernel as the most basic offering. When moving from MCUs to 32- and 64-bit application processors, you'll tend to find RTOSes such as Wind River VxWorks and Wind River Linux, Green Hills' Integrity OS, and even Linux with PREEMPT_RT kernel extensions. These full-blown OSes offer a large selection of software, providing solutions for both real-time scheduling requirements as well as general computing tasks. Even with the OSes we've just rattled off, we've only scratched the surface of what's available. There are free and paid solutions (some costing well over USD$10,000) at all levels of RTOSes, big and small.

So, why would you choose to pay for a solution when there is something available for free? The main differentiating factors between freely available RTOS solutions and paid solutions are safety approvals, middleware, and customer support. BecauseRTOSes provide a highly deterministic execution environment, they are oftenused in complex safety-critical applications. By safety critical, we generally mean a system whose failure could harm people or cause significant damage. These systems require deterministic operation because they must behave in a predictable way all the time. Guaranteeing the code responds to events within a fixed amount of time is a significant step toward ensuring they behave consistently. Most of these safety-critical applications are regulated and have their own sets of governing bodies and standards, such as DO-178B and DO-178C for aircraft or IEC 61508 SIL 3 and ISO 26262 ASILD for industrial applications. To make safety-critical certifications more affordable, designers will typically either keep code for these systems extremely simple (so it is possible to prove mathematically that the system will function consistently and nothing can go wrong) or turn to a commercial RTOS solution,which has been through certification already, as a starting point. WITTENSTEIN SafeRTOS is a derivative of FreeRTOS that carries approvals for industrial, medical, and automotive use.

Middleware can also be an extremely important component in complex systems. Middleware is code that runs between the user code (code that you,the application programmer, write) and lower layers, such as the RTOS or bare metal (no RTOS). Another value proposition of paid solutions is that the ecosystem offers a suite of pre-integrated high-quality middleware (such as filesystems, networking stacks, GUI frameworks, industrial protocols, and so on) that minimizes development and reduces overall project risk. The reason for using middleware, rather than rolling your own, is to reduce the amount of original code being written by an in-house development team. This reduces both the risk and the total time spent by the team—so it can be a worth-while investment, depending on factors such as project complexity and schedule requirements.

Paid solutions will also typically come with some level of customer support directly from the firmware vendor. Engineers are expensive to hire and keep on staff. There's nothing a manger dreads more than walking into a room full of engineers who are puzzling over their tools, rather than working on the real problems that need to be solved. Having expert help that is an email or phone call away can increase a team's productivity dramatically, which leads to a shorter turnaround and a happier workplace for everyone.

FreeRTOS has both paid support and training options, as well as paid middleware solutions, that can be integrated. However, there are also open source and/or freely available middleware components available, some of which will be discussed in this book.

The RTOS used in this book

With all of the options available, you might be wondering: why is it that this book is only covering one RTOS on a single model of MCU? There are a few reasons, one being that most of the concepts we'll cover are applicable to nearly any RTOS available, in the same way that good coding habits transcend the language you happen to be coding. By focusing on a single implementation of an RTOS with a single MCU, we'll be able to dive into topics in more depth than would have been possible if all of the alternatives were also attempted to be discussed.

FreeRTOS is one of the most popular RTOS implementations for MCUs and is very widely available. It has been around for over 15 years and has been ported to dozens of platforms . If you've ever spoken to a true low-level embedded systems engineer who is familiar with RTOS programming, they've certainly heard of FreeRTOS and have likely used it at least once. By focusing our attention on FreeRTOS, you'll be well-positioned to quickly migrate your knowledge of FreeRTOS to other hardware or to transition to another RTOS, if the situation calls for it.

The other reason we're using FreeRTOS? Well, it's FREE! FreeRTOS is distributed under the MIT license. See https://www.freertos.org/a00114.html for more details on licensing and other FreeRTOS derivatives, such as SAFERTOS and OpenRTOS.

The following is a diagram showing where FreeRTOS sits in a typicalARM firmware stack. Stack refers to all of the different layers of firmware components that make up the system and how they are stacked on top of one another. A user in this context refers to the programmer using FreeRTOS (rather than the end user of the embedded system):

Some noteworthy items are as follows:

User code

is able to access the same FreeRTOS API, regardless of the underlying hardware port implementation.

FreeRTOS

does not prevent

User code

from using vendor-supplied drivers, CMSIS, or raw hardwareregisters.

Having a standardized API that is consistent across hardware means code can be easily migrated between hardware targets, without beingconstantly rewritten. The ability to have code talk directly to hardware also provides the means to write extremelyefficient code when necessary (at the expense ofportability).

Now that we know what an RTOS is, let's have a closer look at when it is appropriate to use an RTOS.

Deciding when to use an RTOS

Occasionally, when someone first learns of the term real-time OS, they mistakenly believe that an RTOS is the only way to achieve real-time behavior in an embedded system. While this is certainly understandable (especially given the name) it couldn't be further from the truth. Sometimes, it is best to think of an RTOS as a potential solution, rather than the solution to be used for everything. Generally speaking, for an MCU-based RTOS to be the ideal solution for a given problem, it needs to have a Goldilocks-level of complexity—not too simple, but not too complicated.

If there is an extremely simple problem, such as monitoring two states and triggering an alert when they are both present, the solution could be a straightforward hardware solution (such as an AND gate). In this case, there may be no reason to complicate things further, since the AND gate solution is going to be very fast, with high determinism and extreme reliability. It will also require very little development time.

Now, consider a case where there are only one or two tasks to be performed, such as controlling the speed of a motor and watching an encoder to ensure the correct distance is traversed. This could certainly be implemented in discrete analog and digital hardware, but having a configurable distance would add some complexity. Additionally, tuning the control loop coefficients would likely require twiddling the potentiometer settings (possibly for each individual board), which is undesirable in some or most cases, by today's manufacturing standards. So, on the hardware solution side, we're left with a CPLD or FPGA to implement the motion control algorithm and track the distance traveled. This happens to be a very good fit for either, since it is potentially small enough to fit into a CPLD, but in some cases, the cost of an FPGA might be unacceptable. This problem is also handled by MCUs regularly. If existing in-house resources don't have the expertise required with hardware languages or toolchains, then a bare-metal MCU firmware solution is probably a good fit.

Let's say the problem is more complicated, such as a device that controls several different actuators, reads data from a range of sensors, and stores those values in local storage. Perhaps the device also needs to sit on some sort of network, such as Ethernet, Wi-Fi, controller area network (CAN), and so on. An RTOS can solve this type of problem quite well. The fact that there are many different tasks that need to be completed, more or less asynchronously to one another, makes it very easy to argue that the additional complexity the RTOS brings will pay off. The RTOS helps us to ensure the lower priority, more complex tasks, such as networking and the filesystem stacks, won't interfere with the more time-critical tasks (such as controlling actuators and reading sensors). In many cases, there may be some form of control system that generally benefits from being run at well-defined intervals in time—a strength of the RTOS.

Now, consider a similar system to the previous one, but now there are multiple networking requirements, such as serving a web page, dealing with user authentication in a complex enterprise environment, and pushing files to various shared directories that require different network-based file protocols. This level of complexity can be achieved with an RTOS, but again, depending on the available team resources, this might be better left to a full-blown OS to handle (either RTOS or general-purpose), since many of the complex software stacks required already exist. Sometimes, a multi-core approach might be taken, with one of the cores running an RTOS and the other running a general-purpose OS.

By now, it is probably obvious that there is no definitive way to determine exactly which real-time solution is correct for all cases. Each project and team will have their own unique requirements, backgrounds, skill-sets, and contexts that set the stage for this decision. There are many factors that go into selecting a solution to a problem; it is important to keep an open mind and to choose the solution that is best for your team and project at that point in time.