Tải bản đầy đủ (.pdf) (5 trang)

HandBooks Professional Java-C-Scrip-SQL part 184 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (33.23 KB, 5 trang )

void
delay(unsigned int nMilliseconds)
{
#define CYCLES_PER_MS 260 /* Number of decrement-and-test cycles. */
unsigned long nCycles = nMilliseconds * CYCLES_PER_MS;
while (nCycles );
} /* delay() */
The hardware-specific constant CYCLES_PER_MS represents the number of
decrement-and-test cycles (nCycles != 0) that the processor can perform in a
single millisecond. To determine this number I used trial and error. I made an
approximate calculation (I think it came out to around 200), then wrote the
remainder of the program, compiled it, and ran it. The LED was indeed blinking
but at a rate faster than 1 Hz. So I used my trusty stopwatch to make a series of
small changes to CYCLES_PER_MS until the rate of blink was as close to 1 Hz as
I cared to test.
That's it! That's all there is to the Blinking LED program. The three functions
main, toggleLed, and delay do the whole job. If you want to port this program to
some other embedded system, you should read the documentation that came with
your hardware, rewrite toggleLed as necessary, and change the value of
CYCLES_PER_MS. Of course, we do still need to talk about how to build and
execute this program. We'll examine those topics in the next two chapters. But
first, I have a little something to say about infinite loops and their role in embedded
systems.
2.3 The Role of the Infinite Loop
One of the most fundamental differences between programs developed for
embedded systems and those written for other computer platforms is that the
embedded programs almost always end with an infinite loop. Typically, this loop
surrounds a significant part of the program's functionality—as it does in the
Blinking LED program. The infinite loop is necessary because the embedded
software's job is never done. It is intended to be run until either the world comes to
an end or the board is reset, whichever happens first.


In addition, most embedded systems have only one piece of software running on
them. And although the hardware is important, it is not a digital watch or a cellular
phone or a microwave oven without that embedded software. If the software stops
running, the hardware is rendered useless. So the functional parts of an embedded
program are almost always surrounded by an infinite loop that ensures that they
will run forever.
This behavior is so common that it's almost not worth mentioning. And I wouldn't,
except that I've seen quite a few first-time embedded programmers get confused by
this subtle difference. So if your first program appears to run, but instead of
blinking the LED simply changes its state once, it could be that you forgot to wrap
the calls to toggleLed and delay in an infinite loop.
[1] Of course, the rate of blink is completely arbitrary. But one of the things I like
about the 1 Hz rate is that it's easy to confirm with a stopwatch. Simply start the
stopwatch, count off some number of blinks, and see if the number of elapsed
seconds is the same as the number of blinks. Need greater accuracy? Simply count
off more blinks.
[2] Unfortunately, the exact syntax of inline assembly varies from compiler to
compiler. In the example, I'm using the format preferred by the Borland C++
compiler. Borland's inline assembly format is one of the best because it supports
references to variables and constants that are defined within the C code.
Chapter 3. Compiling, Linking, and Locating
 3.1 The Build Process
 3.2 Compiling
 3.3 Linking
 3.4 Locating
 3.5 Building das Blinkenlights
I consider that the golden rule requires that if I like a program I must share it with
other people who like it. Software sellers want to divide the users and conquer
them, making each user agree not to share with others. I refuse to break solidarity
with other users in this way. I cannot in good conscience sign a nondisclosure

agreement or a software license agreement. So that I can continue to use
computers without dishonor, I have decided to put together a sufficient body of free
software so that I will be able to get along without any software that is not free.
—Richard Stallman, Founder of the GNU Project, The GNU Manifesto
In this chapter, we'll examine the steps involved in preparing your software for
execution on an embedded system. We'll also discuss the associated development
tools and see how to build the Blinking LED program shown in Chapter 2. But
before we get started, I want to make it clear that embedded systems programming
is not substantially different from the programming you've done before. The only
thing that has really changed is that each target hardware platform is unique.
Unfortunately, that one difference leads to a lot of additional software complexity,
and it's also the reason you'll need to be more aware of the software build process
than ever before.
3.1 The Build Process
There are a lot of things that software development tools can do automatically
when the target platform is well defined.
[1]
This automation is possible because the
tools can exploit features of the hardware and operating system on which your
program will execute. For example, if all of your programs will be executed on
IBM-compatible PCs running DOS, your compiler can automate—and, therefore,
hide from your view—certain aspects of the software build process. Embedded
software development tools, on the other hand, can rarely make assumptions about
the target platform. Instead, the user must provide some of his own knowledge of
the system to the tools by giving them more explicit instructions.
The process of converting the source code representation of your embedded
software into an executable binary image involves three distinct steps. First, each
of the source files must be compiled or assembled into an object file. Second, all of
the object files that result from the first step must be linked together to produce a
single object file, called the relocatable program. Finally, physical memory

addresses must be assigned to the relative offsets within the relocatable program in
a process called relocation. The result of this third step is a file that contains an
executable binary image that is ready to be run on the embedded system.
The embedded software development process just described is illustrated in Figure
3-1. In this figure, the three steps are shown from top to bottom, with the tools that
perform them shown in boxes that have rounded corners. Each of these
development tools takes one or more files as input and produces a single output
file. More specific information about these tools and the files they produce is
provided in the sections that follow.
Figure 3-1. The embedded software development process

Each of the steps of the embedded software build process is a transformation
performed by software running on a general-purpose computer. To distinguish this
development computer (usually a PC or Unix workstation) from the target
embedded system, it is referred to as the host computer. In other words, the
compiler, assembler, linker, and locator are all pieces of software that run on a host
computer, rather than on the embedded system itself. Yet, despite the fact that they
run on some other computer platform, these tools combine their efforts to produce
an executable binary image that will execute properly only on the target embedded
system. This split of responsibilities is shown in Figure 3-2.
Figure 3-2. The split between host and target

In this chapter and the next I'll be using the GNU tools (compiler, assembler,
linker, and debugger) as examples. These tools are extremely popular with
embedded software developers because they are freely available (even the source
code is free) and support many of the most popular embedded processors. I will
use features of these specific tools as illustrations for the general concepts
discussed. Once understood, these same basic concepts can be applied to any
equivalent development tool.
3.2 Compiling

The job of a compiler is mainly to translate programs written in some human-
readable language into an equivalent set of opcodes for a particular processor. In
that sense, an assembler is also a compiler (you might call it an "assembly
language compiler") but one that performs a much simpler one-to-one translation
from one line of human-readable mnemonics to the equivalent opcode. Everything
in this section applies equally to compilers and assemblers. Together these tools
make up the first step of the embedded software build process.
Of course, each processor has its own unique machine language, so you need to
choose a compiler that is capable of producing programs for your specific target
processor. In the embedded systems case, this compiler almost always runs on the
host computer. It simply doesn't make sense to execute the compiler on the
embedded system itself. A compiler such as this—that runs on one computer
platform and produces code for another—is called a cross-compiler. The use of a
cross-compiler is one of the defining features of embedded software development.
The GNU C/C++ compiler ( gcc ) and assembler (as ) can be configured as either
native compilers or cross-compilers. As cross-compilers these tools support an
impressive set of host-target combinations. Table 3-1 lists some of the most
popular of the supported hosts and targets. Of course, the selections of host
platform and target processor are independent; these tools can be configured for
any combination.
Table 3-1. Hosts and Targets Supported by the GNU Compiler
Host Platforms Target Processors
DEC Alpha Digital Unix
HP 9000/700 HP-UX
IBM Power PC AIX
IBM RS6000 AIX
SGI Iris IRIX
Sun SPARC Solaris
Sun SPARC SunOS
X86 Windows 95/NT

X86 Red Hat Linux

AMD/Intel x86 (32-bit only)
Fujitsu SPARClite
Hitachi H8/300, H8/300H, H8/S
Hitachi SH
IBM/Motorola PowerPC
Intel i960
MIPS R3xxx, R4xx0
Mitsubishi D10V, M32R/D
Motorola 68k
Sun SPARC, MicroSPARC
Toshiba TX39
Regardless of the input language (C/C++, assembly, or any other), the output of the
cross-compiler will be an object file. This is a specially formatted binary file that
contains the set of instructions and data resulting from the language translation
process. Although parts of this file contain executable code, the object file is not
intended to be executed directly. In fact, the internal structure of an object file
emphasizes the incompleteness of the larger program.
The contents of an object file can be thought of as a very large, flexible data
structure. The structure of the file is usually defined by a standard format like the

×