One of the more surprising developments of the last few decades has been the ascendance of computers to a position of prevalence in human affairs. Today there are more computers in our homes and offices than there are people who live and work in them. Yet many of these computers are not recognized as such by their users. In this chapter, I’ll explain what embedded systems are and where they are found. I will also introduce the subject of embedded programming, explain why I have selected C and C++ as the languages for this book, and describe the hardware used in the examples.

What is an Embedded System?

An embedded system is a combination of computer hardware and software, and perhaps additional mechanical or other parts, designed to perform a specific function.

A good example of an embedded system is the microwave oven. Almost every household has one, and tens of millions of them are used every day, but very few people realize that there is a processor and software involved in the preparation of their lunch or dinner.

This is in direct contrast to the personal computer in the family room. It too is comprised of computer hardware and software and mechanical components (disk drives, for example). However, a personal computer is not designed to perform a specific function. Rather, it is able to do many different things. Many people use the term general-purpose computer to make this distinction clear. As shipped, a general-purpose computer is a blank slate; the manufacturer does not know what the customer will do with it. One customer may use it for a network fileserver, another may use it exclusively for playing games, while a third may use it to write the next great American novel.

Frequently, an embedded system is a component within some larger system. For example, modern cars and trucks contain many embedded systems. There may be one embedded system to control the anti-lock brakes, another to monitor and control the vehicle’s emissions, and a third to display information on the dashboard. In some cases, these embedded systems may be connected by some sort of a communications network, but that is certainly not a requirement.

At the possible risk of confusing you, it is important to point out that a general-purpose computer is itself made up of numerous embedded systems. For example, my computer consists of a keyboard, mouse, video card, modem, hard drive, floppy drive, and sound card—each of which is an embedded system. Each of these devices contains a processor and software and is designed to perform a specific function. For example, the modem is designed to send and receive digital data over an analog telephone line. That’s it. And all of the other devices can be summarized in a single sentence as well.

If an embedded system is designed well, the existence of the processor and software may be completely unnoticed by a user of the device. Such is the case for a microwave oven, VCR, or alarm clock. In some cases, it would even be possible to build an equivalent device that does not contain the processor and software. This could be done by replacing the combination with a custom integrated circuit that performs the same function(s) in hardware. However, a lot of flexibility is lost when a design is hard-coded in this way. It is much easier, and cheaper, to change a few lines of software than to redesign a piece of custom hardware.

History and Future

Given the definition of embedded systems above, the first such systems could not possibly have appeared before 1971. That was the year Intel introduced the world’s first microprocessor. This chip, the 4004, was designed for use in a line of business calculators produced by the Japanese company Busicom. In 1969, Busicom asked Intel to design a set of custom integrated circuits—one for each of their new calculator models. The 4004 was Intel’s response. Rather than design custom hardware for each calculator, Intel proposed a general-purpose circuit that could be used throughout the entire line of calculators. This general-purpose processor was designed to read and execute a set of instructions—software—stored in an external memory chip. Intel’s idea was that the software would give each calculator its unique set of features.

The microprocessor was an overnight success and their use increased steadily over the next decade. Early embedded applications included unmanned space probes, computerized traffic lights, and aircraft flight control systems. In the 1980’s, embedded systems quietly rode the waves of the microcomputer age and brought microprocessors into every part of our personal and professional lives. Many of the electronic devices in our kitchens (bread machines, food processors, and microwave ovens), living rooms (televisions, stereos, and remote controls), and workplaces (fax machines, pagers, laser printers, cash registers, and credit card readers) are embedded systems. And during the same time period, the medical and military communities have come up with a myriad of new devices that could not have been designed in any other way.

It seems inevitable that the number of embedded systems will continue to increase rapidly. Already there are promising new embedded devices with enormous market potential: light-switches and thermostats that can be controlled by a central computer; intelligent air-bag systems that don’t inflate when children or small adults are present; palm-sized electronic organizers and personal digital assistants (PDAs); digital cameras; and dashboard navigation systems. Individuals who possess the skills and desire to design the next generation of embedded systems will be in demand for quite some time.

Real-Time Systems

One subclass of embedded systems is worthy of an introduction at this point. As commonly defined, a real-time system is an embedded system that has timing constraints. In other words, a real-time system is partially specified in terms of its ability to make certain calculations or decisions in a timely manner. These important calculations are said to have deadlines for completion. And, for all practical purposes, a missed deadline is just as bad as a wrong answer.

The issue of what happens if a deadline is missed is a crucial one. For example, if the real-time system is part of an airplane's flight control system, it is possible that the lives of the passengers and crew will be endangered by a single missed deadline. However, if the system is instead involved in satellite communication the damage may be limited to a corrupt data packet. The more severe the consequences, the more likely it will be said that the deadline is hard and, thus, the system hard real-time. Real-time systems at the other end of this continuum are said to have soft deadlines.

All of the topics and examples presented in this book are also applicable to the designers of real-time systems. However, the designer of a real-time system must be more diligent in his work. He must guarantee reliable operation of the software and hardware under all possible conditions. And, to the degree that human lives depend upon its proper execution, this guarantee must be backed up by engineering calculations and descriptive paperwork.

Variations on the Theme

Unlike software designed for general-purpose computers, embedded software cannot usually be run on other embedded systems without significant modification. This is primarily because of the incredible variety in the underlying hardware. The hardware in each embedded system is tailored specifically to the application, in order to keep system costs low. As a result, unnecessary circuitry is eliminated and hardware resources are shared wherever possible. In this section you will learn what hardware features are common across all embedded systems and why there is so much variation with respect to just about everything else.

By definition all embedded systems contain a processor and software, but what other features do they have in common? Certainly, in order to have software, there must be a place to store the executable code and temporary storage for run-time data manipulation. These take the form of ROM and RAM, respectively; any embedded system will have some amount of each. If only a small amount of memory is required, it may be contained within the same chip as the processor. Otherwise, one or both types of memory will reside in external memory chips.

All embedded systems also contain some type of inputs and outputs. For example, in a microwave oven the inputs are the buttons on the front-panel and a temperature probe, while the outputs are the human-readable display and the microwave radiation. It is almost always the case that the outputs of the embedded system are a function of its inputs and several other factors (elapsed time, current temperature, etc.). The inputs to the system usually take the form of sensors and probes, communication signals, or control knobs and buttons. The outputs are typically displays, communications signals, or changes to the physical world.

Figure 1-1. A Generic Embedded System

With the exception of these few common features, the rest of the embedded hardware is usually unique. This variation is the result of many competing design criteria. Each system must meet a completely different set of requirements, any or all of which may affect the compromises and tradeoffs made during the development of the product. For example, if the system must have a production cost below $10, then other things—like processing power and system reliability—may need to be sacrificed in order to meet that goal.

Of course, production cost is just one of the possible constraints under which embedded hardware designers work. Other common design requirements are:

Processing Power

Processing power is the amount of processing power necessary to get the job done. A common way to compare processing power is the MIPS (millions of instructions per second) rating. If two processors have ratings of 25 MIPS and 40 MIPS, the latter is said to be the more powerful of the two. However, there are also other important features of the processor to be considered. One of these is the register width, which typically ranges from 8 to 64 bits. Today’s general-purpose computers use 32 and 64-bit processors exclusively, but embedded systems are still commonly built with the older and less costly 8 and 16-bit processors.


Memory is the amount of ROM and RAM required to hold the executable software and the data it manipulates. Here the hardware designer must usually make his best estimate up front and be prepared to increase or decrease the actual amount as the software is being developed. The amount of memory required may also affect the processor selection. In general, the register width of a processor establishes the upper limit of the amount of memory it can access (e.g., an 8-bit address register can select one of only 256 unique memory locations).

Development Cost

The development cost is the cost of the hardware and software design processes. This is a fixed, one-time cost, so it may be that money is no object (usually for high-volume, high-profit products) or that this is the only accurate measure of system cost (in the case of a small number of systems produced).

Number of Units

- the tradeoff between production cost and development cost is affected most by the number of units expected to be produced and sold. For example, it is usually undesirable to develop your own custom hardware components for a low-volume product.

Expected Lifetime

The expected lifetime is how long the system should continue to function (on average): a month, a year, or a decade? This affects all sorts of design decisions from the selection of hardware components to how much the system may cost to develop and/or produce.


Finally, how reliable must the final product be? If it is a children’s toy, it doesn’t always have to work right, but if it’s a part of the space shuttle or a car it had sure better do what it is supposed to each and every time.

In addition to these general requirements, there are the detailed functional requirements of the system itself. These are the things that give the embedded system its unique identity as a microwave oven, pacemaker, or pager.

Table 1-1 illustrates the range of possible values for each of the above constraints. These are just estimates and should not be taken too seriously. In some cases, two or more of the criteria may be linked. For example, increases in processing power may lead to increased production costs. Conversely, we might imagine that the same change in processing power would have the effect of decreasing the development costs—by reducing the complexity of the hardware and/or software design. So the values in a particular column do not necessarily go together.

Criterion Low Medium High
Processing Power 4- or 8-bit processor 16-bit processor 32- or 64-bit processor
Memory < 16 Kbytes 16 Kbytes to 1 Mbyte > 1 Mbyte
Development Cost < $100,000 $100,000-$1,000,000 > $1,000,000
Production Cost < $10 $10-$1,000 > $1,000
Number of Units < 100 100-10,000 > 10,000
Expected Lifetime Days, Weeks, or Months Years Decades
Reliability May Occasionally Fail Works Reliably Fail-proof

Table 1-1 Common Design Requirements for Embedded Systems

In order to simultaneously demonstrate the variation from one embedded system to the next and the possible effects of these design requirements on the hardware, I will now take some time to describe three embedded systems in some detail. My goal is to put you in the system designer’s shoes for a few moments before beginning to narrow our discussion to embedded software development.

Digital Watch

At the end of the evolutionary path that began with sundials, water clocks, and hourglasses is the digital watch. Among its many features are the presentation of the date and time (usually to the nearest second), the measurement of the length of an event to the nearest hundredth of a second, and the generation of an annoying little sound at the beginning of each hour. As it turns out, these are very simple tasks that do not require very much processing power or memory. In fact, the only reason to employ a processor at all is to support a range of models and features from a single hardware design.

The typical digital watch contains a simple, inexpensive 8-bit processor. Because such small processors cannot address very much memory this type of processor usually contains its own on-chip ROM. And, if there are sufficient registers available, this application may not require any RAM at all. In fact, all of the electronics—processor, memory, counters and real-time clocks—are likely to be stored in a single chip. The only other hardware elements of the watch are the inputs (buttons) and outputs (LCD and speaker).

The watch designer’s goal is to create a reasonably reliable product with an extraordinarily low production cost. If, after production, the watch is found to keep more reliable time than most it can be sold under a brand name with a higher markup. Otherwise, a profit can still be made by selling the watch through a discount sales channel. For even lower-cost versions, the stopwatch buttons or speaker could be eliminated. This would limit the functionality of the watch, but might not even require any software changes. And, of course, the cost of all this development effort may be fairly high, since it will be split over hundreds of thousands or even millions of watch sales.

Video Game Player

When you pull the Nintendo-64 out from your entertainment center, you are preparing to use an embedded system. In some cases, these machines are more powerful than the comparable generation of personal computers. Yet video game players for the home market are relatively inexpensive compared to personal computers. It is the competing requirement of high processing power vs. low production cost that keeps video game designers awake at night, and their children well-fed.

The companies that produce video game players don’t usually care how much it costs to develop the system, so long as the production costs of the resulting product are low—typically around a hundred dollars. They may even encourage their engineers to design custom processors at a development cost of hundreds of thousands of dollars each. So, although there may be a 64-bit processor inside your video game player, it is not necessarily the same type of processor that would be found in a 64-bit personal computer. In all likelihood, the processor is highly specialized for the demands of the video games it is intended to play.

Since production cost is so crucial in the home video game market, the designers may also use tricks to shift the costs around. For example, one common tactic is to move as much of the memory and other peripheral electronics as possible off of the main circuit board and onto the game cartridges. This helps to reduce the cost of the game player but increases the price of each and every game. So, while the system may have a powerful 64-bit processor, it may have only a few megabytes of memory on the main circuit board. This is just enough memory to bootstrap the machine to a state from which it can access additional memory on the game cartridge.

Mars Explorer

In 1976, two unmanned spacecraft arrived on the planet Mars. As a part of their mission they were to collect samples of the Martian surface, analyze the chemical makeup of each, and transmit the results to scientists back on Earth. Those Viking missions are amazing to me. Surrounded by personal computers that must be rebooted almost daily, it is remarkable to think that more than 20 years ago a team of scientists and engineers successfully built two computers that survived a journey of 34 million miles and functioned correctly for half a decade. Clearly, reliability was one of the most important requirements for these systems.

What if a memory chip had failed? Or the software had bugs that caused it to crash? Or an electrical connection broke during impact? There is no way to prevent such problems from occurring. So, all of these potential failure points and many others had to be eliminated by adding redundant circuitry or extra functionality: an extra processor here, special memory diagnostics there, a hardware timer to reset the system if the software got stuck, and so on.

More recently, NASA launched the Pathfinder mission. Its primary goal was to demonstrate the feasibility of getting to Mars on a budget. Of course, given the advances in technology made since the mid-70’s, the designers didn’t have to give up too much to accomplish this. They may have reduced the amount of redundancy somewhat, but Pathfinder still had more processing power and memory than Viking ever could have. The Mars Pathfinder is actually two embedded systems: a landing-craft and a rover. The landing-craft has a 32-bit processor and 128 Mbytes of RAM; the rover, on the other hand, has only an 8-bit processor and 512 Kbytes. These choices probably reflect the different functional requirements of the two systems. But I’m sure that production cost wasn’t much of an issue in either case.

C: The Least Common Denominator

One of the few constants across all these systems is the use of the C programming language. More than any other, C has become the language of embedded programmers. This has not always been the case, and it will not continue to be so forever. However, at this time, C is the closest thing there is to a standard in the embedded world. In this section I’ll explain why C has become so popular and why I have chosen it and its descendent C++ as the primary languages of this book.

Since successful software development is so frequently about selecting the best language for a given project, it is surprising to find that one language has proven itself appropriate for both 8-bit and 64-bit processors; in systems with bytes, kilobytes, and megabytes of memory; and for development teams consisting of from one to a dozen or more people. Yet this is precisely the range of projects in which C has thrived.

Of course, C is not without advantages. It is small and fairly simple to learn, compilers are available for almost every processor in use today, and there is a very large body of experienced C programmers. In addition, C has the benefit of processor-independence, which allows programmers to concentrate on algorithms and applications, rather than on the details of a particular processor architecture. However, many of these advantages apply equally to other high-level languages. So why has C succeeded where so many other languages have largely failed?

Perhaps the greatest strength of C—and the thing that sets it apart from languages like Pascal and FORTRAN—is that it is a very “low-level” high-level language. As we shall see throughout the book, C gives embedded programmers an extraordinary degree of direct hardware control without sacrificing the benefits of high-level languages. The “low-level” nature of C was a clear intention of the language’s creators. In fact, Kernighan and Ritchie included the following comment on the opening page of their book The C Programming Language:

"C is a relatively “low level” language. This characterization is not pejorative; it simply means that C deals with the same sort of objects that most computers do. These may be combined and moved about with the arithmetic and logical operators implemented by real machines.

Few popular high-level languages can compete with C in the production of compact, efficient code for almost all processors. And, of these, only C allows programmers to interact with the underlying hardware so easily.

Other Embedded Languages

Of course, C is not the only language used by embedded programmers. At least four other languages—assembly, C++, Ada, and Java—are worth mentioning in greater detail.

In the early days, embedded software was written exclusively in the assembly language of the target processor. This gave programmers complete control of the processor and other hardware, but at a price. Assembly languages have many disadvantages, not the least of which are higher software development costs and a lack of code portability. In addition, finding skilled assembly programmers has become much more difficult in recent years. Assembly is now used primarily as an adjunct to the high-level language; usually only for those small pieces of code that must be extremely efficient or ultra-compact, or cannot be written in any other way.

C++ is an object-oriented superset of C that is increasingly popular among embedded programmers. All of the core language features are the same as C, but C++ adds new functionality for better data abstraction and a more object-oriented style of programming. These new features are very helpful to software developers, but some of them do reduce the efficiency of the executable program. So C++ tends to be most popular with large development teams, where the benefits to developers outweigh the loss of program efficiency.

Ada is also an object-oriented language, though it is substantially different than C++. Ada was originally designed by the US Department of Defense for the development of mission-critical military software. Despite being twice accepted as an international standard (Ada83 and Ada95), it has not gained much of a foothold outside of the defense and aerospace industries. And it is losing ground there in recent years. This is unfortunate, as the Ada language does have many features that would simplify embedded software development if used instead of C++.

One of the most exciting things about working in the software industry is the rapid pace of technological change. Indeed, in other parts of our industry, the Java programming language has begun to displace C and C++ significantly. However, despite a lot of promise, it is too soon to tell if the same thing will happen in embedded software. In an article in the May 1997 issue of Embedded Systems Programming, Brian Frank and I examined the state of Java tools for embedded programmers. We concluded that Java wasn’t yet viable for embedded software development. But neither of us would be too surprised if the tool situation improved dramatically over the next year or two.

Choosing a Language for the Book

A major question facing the author of a book like this is: Which programming languages should be included in the discussion? Attempting to cover too many languages might confuse the reader or detract from more important points. On the other hand, focusing too narrowly could make the discussion unnecessarily academic or (worse for the author and publisher) limit the potential market for the book.

Certainly, C must be the centerpiece of any book about embedded programming—and this book will be no exception. More than half of the sample code is written in C and the discussion will focus primarily on C-related programming issues. Of course, everything that is said about C programming applies equally to C++. In addition, I will cover those features of C++ that are most useful for embedded software development and use them in the later examples. Assembly language will be discussed in certain limited contexts, but will be avoided whenever possible. In other words, I will mention assembly language only when a particular programming task cannot be accomplished in any other way.

I feel that this mixed treatment of C, C++, and assembly most accurately reflects how embedded software is actually developed today and how it will continue to be developed in the near term future. I hope that this choice will keep the discussion clear, provide information that is useful to people developing actual systems, and include as large a potential audience as possible.

A Few Words About Hardware

It is the nature of programming that books about the subject must include examples. Typically, these examples are selected so that they can be easily experimented with by interested readers. That means readers must have access to the very same software development tools and hardware platforms used by the author. Unfortunately, in the case of embedded programming, this is unrealistic. It simply does not make sense to run any of the example programs on the platforms available to most readers—PCs, Macs, and UNIX workstations.

Even selecting a standard embedded platform is difficult. As you have already learned, there is no such thing as a “typical” embedded system. Whatever hardware is selected, the majority of readers will not have access to it. But despite this rather significant problem, I do feel it is important to select a reference hardware platform for use in the examples. In so doing, I hope to make the examples consistent and, thus, the entire discussion more clear.

In order to illustrate as many points as possible with a single piece of hardware, I have found it necessary to select a middle-of-the-road platform. This hardware consists of a 16-bit processor (Intel’s 80188EB), a decent amount of memory (128 Kbytes of RAM and 256 Kbytes of ROM), and some common types of inputs, outputs, and peripheral components. The board I’ve chosen is called the Target188EB and is manufactured and sold by Arcom Control Systems. More information about the Arcom board and instructions for obtaining one can be found in Appendix A.

If you have access to the reference hardware, you will be able to work through the examples in the book exactly as they are presented. Otherwise, you will need to port the example code to an embedded platform that you do have access to. Toward that end, every effort has been made to make the example programs as portable as possible. However, the reader should bear in mind that the hardware in each embedded system is different and that some of the examples may be meaningless on his hardware. For example, it wouldn’t make sense to port the flash memory driver presented in Chapter 6 to a board with no flash memory devices.

Anyway I’ll have a lot more to say about hardware in Chapter 5. But first we have a number of software issues to discuss. So let’s get started.