• 沒有找到結果。

µC/OS-II Goals Preface

N/A
N/A
Protected

Academic year: 2022

Share "µC/OS-II Goals Preface"

Copied!
305
0
0

加載中.... (立即查看全文)

全文

(1)

Preface

My first book, “ µC/OS, The Real-Time Kernel ” is now 6 years old and the publisher has sold well over 15,000 copies around the world. When I was asked to do a second edition, I thought it would be a fairly straightforward task; a few corrections here and there, clarify a few concepts, add a function or two to the kernel, etc. If you have a copy of the first edition, you will notice that

µC/OS-II, The Real-Time Kernel” is in fact a major revision. For some strange reason, I wasn’ t satisfied with minor corrections. Also, when my publisher told me that this time, the book would be a ‘ hard cover’, I really wanted to give you your moneys worth. In all, I added more than 200 new pages, and re-wrote the majority of the pages I did keep. I added a porting guide to help you port µC/OS-II to the processor of your choice. Also, I added a chapter that will guide you through upgrading a µC/OS port to µC/OS-II.

The code for µC/OS-II is basically the same as that of µC/OS except that it contains a number of new and useful features, is much better commented, and should be easier to port to processor architectures. µC/OS-II offers all the features provided in µC/OS as well as the following new features:

• A fixed-sized block memory manager,

• A service to allow a task to suspend its execution for a certain amount of time (specified in hours, minutes, seconds and milliseconds),

• User definable ‘ callout’ functions that are invoked when:

a task is created,

a task is deleted,

a context switch is performed, a clock tick occurs.

• A new task create function that provides additional features,

• Stack checking,

• A function returning the version of µC/OS-II,

• And more.

µC/OS-II Goals

Probably the most important goal of µC/OS-II was to make it backward compatible with µC/OS (at least from an application’s standpoint). A µC/OS port might need to be modified to work with µC/OS-II but at least, the application code should require only minor changes (if any). Also, because µC/OS-II is based on the same core as µC/OS, it is jus t as reliable. I added conditional compilation to allow you to further reduce the amount of RAM (i.e. data space) needed by µC/OS-II. This is especially useful when you have resource limited products. I also added the feature described in the previous s ection and cleaned up the code.

Where the book is concerned, I wanted to clarify some of the concepts described in the first edition and provide additional explanations about how µC/OS-II works. I had numerous requests about doing a chapter on how to port µC/OS and thus, such a chapter has been included in this book for µC/OS-II.

(2)

Intended Audience

This book is intended for embedded system programmers, consultants and students interested in real-time operating systems. µC/OS-II is a high performance, deterministic real-time kernel and can be embedded in commercial products (see Appendix F, Licensing). Instead of writing your own kernel, you should consider µC/OS-II. You will find, as I did, that writing a kernel is not as easy as it first looks.

I’m assuming that you know C and have a minimum knowledge of assembly language. You should also understand microprocessor architectures.

What you will need to use µC/OS-II

The code supplied with this book assumes that you will be using an IBM -PC/AT or compatible (80386 Minimum) computer running under DOS 4.x or higher. The code was compiled with Borland International’s C++ V3.1. You should have about 5 MBytes of free disk space on you hard drive. I actually compiled and executed the sample code provided in this book in a DOS window under Windows 95.

To use µC/OS-II on a different target processor (than a PC), you will need to either port µC/OS-II to that processor yourself or, obtain one from µC/OS-II official WEB site at http://www.uCOS-II.com. You will also need appropriate software development tools such as an ANSI C compiler, an assembler, linker/locator and some way of debugging your application.

The µC/OS Story

Many years ago, I designed a product based on an Intel 80C188 at Dynalco Controls and I needed a real-time kernel.

At my previous employer, I had been using a well known kernel (let’s call it kernel ‘A’) but, found it to be too expensive for the application I was designing. We then found a lower cost kernel ($1000 at the time) and started our design with it. Let’s call this kernel, kernel ‘B’. We spent about two months trying to get a couple of very simple tasks to run. We were calling the vendor almost on a daily basis to get help making this work. The vendor claimed that this kernel was written in C. However, we had to initialize every single object using assembly language code. Although the vendor was very patient, we decided that we had enough of this. Our product was falling behind schedu le and we really didn’t want to spend our time debugging this low cost kernel. It turns out that we were one of this vendor’s first customer and the kernel was really not fully tested and debugged!

To get back on track, we decided to go back and use kernel ‘A’. The cost was about $5000 for development seat and we had to pay a per usage fee of about $200 for each unit that we shipped! This was a lot of money at the time, but it bought us some peace of mind. We got the kernel up and running in about 2 days! Three months into the project, one of our engineers discovered what looked like a bug in the kernel. We sent the code to the vendor and sure enough, the bug was confirmed as being in the kernel. The vendor provided a 90 day warranty but, that had exp ired so, in order to get support, we had to pay an addition $500 per year for ‘maintenance’. We argued with the salesperson for a few months that they should fix the bug since we were actually doing them a favor. They wouldn’t budge! Finally, we gave in, we bought the maintenance contract and the vendor fixed the bug six months later! Yes, six months later. We were furious but most importantly, late delivering our product. In all, it took close to a year to get our product to work reliably with kernel ‘A’. I must admit, however, that we never had any problems with it since.

As this was going on, I naively thought, “Well, it can’t be that difficult to write a kernel. All it needs to do is save and restore processor registers”. That’s when I decided to try it out and write my own (part time at night and on weekends).

It took me about a year to get the kernel to be just as good (and in some ways better) than kernel ‘A’. I didn’t want to start a company and sell it because there were already about 5 0 kernels out there so, why have another one?

I then thought of writing a paper for a magazine. I first went to the “C User’s Journal (CUJ)” (the kernel was written in C) which, I had heard, was offering $100 per published page when other magazines were only paying $75 per page.

My paper had 70 or so pages so, that would be a nice compensation for all the time I spent working on my kernel.

(3)

Unfortunately, the article was rejected! There were two reasons. First, the article was too long and the magazine didn’t want to publish a series. Second, they didn’t want to have ‘another kernel article’.

I then decided to turn to Embedded Systems Programming (ESP) magazine because my kernel was designed for embedded systems. I contacted the editor of ESP (Mr. Tyler Sperry) and told him that I had this kernel I wanted to publish in his magazine. I got the same response from Tyler as I did from the C Journal: “Not another kernel article?”

I told him that this kernel was different, it was preemptive, it was comparable to many commercial kernels and that he could put the source code on the ESP BBS (Bulletin Board Service). I was calling Tyler two or three times a week (basically begging him) until he finally gave in (he was probably tired of having me call him) and decide to publish the article. My article got edited down from 70 pages to about 30 pages and was published in two consecutive months (May 1992 and June 1992). The article was probably the most popular article in 1992. ESP had over 500 downloads of the code from the BBS in the first month. Tyler may have feared for his life because kernel vendors were upset that he published a kernel in his magazine. I guess that these vendors must have recognized the quality and capabilities of µC/OS (was called µCOS then). The article was really the first that exposed the internals of a real-time kernel so, some of the secrets were out.

Just about the time the article came out in ESP, I got a call back from Dr. Bernard Williams at R&D Publications (publisher of CUJ), 6 months after the initial contact with CUJ. He had left a message with my wife and told her that he was interested in the article!??! I called him back and told him something like: “Don’t you think you are a little bit late with this? The article is being published in ESP.” Berney said: “No, No, you don’t understand, because the article is so long, I want to make a book out of it.” Initially, Berney simply wanted to publish what I had (as is) so the book would only have 80 or so pages. I said to him, “If I going to write a book, I want to do it right.” I then spent about 6 months adding contents to what is now know as the first edition. In all, the book had about 250 pages to it. I changed the name of µCOS to µC/OS because ESP readers had been calling it ‘Mucus’which didn’t sound too healthy! Come to think of it, maybe it was a kernel vendor that first came up with the name. Anyway, µC/OS, The Real-Time Kernel was then born. Sales were somewhat slow to start. Berney and I projected that we would sell about 4000 to 5000 copies in the life of the book but at that rate, we would be lucky if it sold 2000 copies. Berney insisted that these things take time to get known so, he continued advertising in CUJ for about a year.

A month or so before the book came out, I went to my first Embedded Systems Conference (ESC) in Santa Clara, CA (September 1992). I then met Tyler Sperry for the first time and I showed him the first draft copy of my book. He very quickly glanced at it and said something like: “Would you like to speak at the next Embedded Systems Conference in Atlanta?” Not knowing any better, I said “Sure, what should I talk about?” He said what about “Using small real-time kernels?” I said “Fine”. On the trip back from California, I was thinking “What did I get myself into? I’ve never spoke in front of a bunch of people before! What if I make a fool of myself? What if what I speak about is common knowledge? Those people pay good money to attend this conference.” For the next six months, I prepared my lecture.

At the conference, I had about 70+ attendees. In the first twenty minutes I must have lost one pound of sweat. After my lecture, I had a crowd of about 15 or so people come up to me and say that they were very pleased with the lecture and liked my book. I got re -invited back to the conference but could not attend the one in Santa Clara that year (i.e.

1993). I was able to attend the next conference in Boston (1994) and I have been a regular speaker at ESC ever since.

For the past couple of years, I’ve been on the conference Advisory Committee. I now do at least 3 lectures at every conference and each have average attendance of between 200 and 300 people! My lectures are almost always ranked amongst the top 10% of the con ference.

To date, we sold well over 15,000 copies or µC/OS, The Real-Time Kernel around the world. I received and answered well over 1000 e-mails from the following countries:

In 1995, µC/OS, The Real-Time Kernel was translated in Japanese and published in a magazine called Interface in Japan. µC/OS has been ported to the following processors:

Analog Devices AD21xx

Advanced Risc Machines ARM6, ARM7 Hitachi 64180, H8/3xx, SH series

Intel 80x86 (Real and PM), Pentium, Pentium-II, 8051, 8052, MCS-251, 80196, 8096 Mitsubishi M16 and M32

(4)

Motorola PowerPC, 68K, CPU32, CPU32+, 68HC11, 68HC16 Philips XA

Siemens 80C166 and TriCore Texas instruments TMS320 Zilog Z-80 and Z-180

And more.

In 1994, I decided to write my second book: Embedded Systems Building Blocks, Complete and Ready -to-Use Modules in C (ESBB) and contains over 600 pages. For some reason, ESBB has not been as popular as µC/OS although it contains as much valuable information not found anywhere else. I always thought that it would b e an ideal book for people just starting in the embedded world.

In 1998, I opened the official µC/OS WEB site www.uCOS-II.com. I intend this site to contain ports, application notes, links, answers to frequently aske d questions (FAQs), upgrades for both µC/OS and µC/OS-II, and more. All I need is time!

Back in 1992, I never imagined that writing an article would have changed my life as it did. I met a lot of very interesting people and made a number of good friends in the process. I still answer every single e-mail that I receive.

I believe that if you take the time to write to me then I owe you a response. I hope you enjoy this book.

Acknowledgements

First and foremost, I would like to thank my wife for her support, encouragement, understanding and especially patience. Manon must have heard the words “Just one more week!” about a dozen times while I was writing this book.

I would also like to thank my children James (age 8) and Sabrina (age 4) for putting up with the long hours I had to spend in front of the computer. I hope one day they will understand.

I would also like to thank Mr. Niall Murphy for carefully reviewing most of the chapters and providing me with valuable feedback. Special thanks to Mr. Alain Chebrou and Mr. Bob Paddock for passing the code for µC/OS-II through a fine tooth comb.

I would like to thank all the fine people at R&D Technical books for their help in making this book a reality, and also for putting up with my insistence on having things done my way.

Finally, I would like to thank all the people who have purchased µC/OS, The Real-Time Kernel as well as Embedded Systems Building Blocks and who, by doing so, have encouraged me to pursue this interesting past-time.

(5)

Introduction

This book describes the design and implementation of µC/OS-II (pronounced "Micro C O S 2") which stands for Micro-Controller Operating System Version 2 . µC/OS -II is based on µC/OS, The Real-Time Kernel which was first published in 1992. Thousands of people around the world are using µC/OS in all kinds of applications such as cameras, medical instruments, musical instruments, engine controls, network adapters, highway telephone call boxes, ATM machines, industrial robots, and many more. Nu merous colleges and Universities have also used µC/OS to teach students about real-time systems.

µC/OS-II is upward compatible with µC/OS (V1.11) but provides many improvements over µC/OS such as the addition of a fixed-sized memory manager, user defina ble callouts on task creation, task deletion, task switch and system tick, supports TCB extensions, stack checking and, much more. I also added comments to just about every function and I made µC/OS -II much easier to port to different processors. The source code in µC/OS was found in two source files. Because µC/OS-II contains many new features and functions, I decided to split µC/OS-II in a few source files to make the code easier to maintain.

If you currently have an application (i.e. product) that runs with µC/OS, your application should be able to run, virtually unchanged, with µC/OS-II. All of the services (i.e. function calls) provided by µC/OS have been preserved.

You may, however, have to change include files and product build files to ‘point’to the new file names.

This book contains ALL the source code for µC/OS-II and a port for the Intel 80x86 processor running in Real-Mode and for the Large Model . The code was developed on a PC running the Microsoft Windows 95 operating system.

Examples run in a DOS compatible box under the Windows 95 environment. Development was done using the Borland International C/C++ compiler version 3.1. Although µC/OS-II was developed and tested on a PC, µC/OS-II was actually targeted for embedded systems and can easily be ported to many different processor architectures.

µC/OS-II features:

Source Code:

As I mentioned previously, this book contains ALL the source code for µC/OS-II. I went through a lot of efforts to provide you with a high quality ‘product’. You may not agree with some of the style constructs that I use but you should agree that the code is both clean and very consistent. Many commercial real-time kernels are provided in source form. I challenge you to find any such code that is as neat, consis tent, well commented and organized as µC/OS-II’s. Also, I believe that simply giving you the source code is not enough. You need to know how the code works and how the different pieces fit together. You will find this type of information in this book. The organization of a real-time kernel is not always apparent by staring at many source files and thousands of lines of code.

Portable:

Most of µC/OS-II is written in highly portable ANSI C, with target microprocessor specific code written in assembly language. Assembly language is kept to a minimum to make µC/OS -II easy to port to other processors. Like µC/OS, µC/OS-II can be ported to a large number of microprocessors as long as the microprocessor provides a stack pointer and the CPU registers can be pushed onto and popped from the stack. Also, the C compiler should either provide in-line assembly or language extensions that allow you to enable and disable interrupts from C. µC/OS-II can run on most 8-bit, 16-bit, 32-bit or even 64-bit microprocessors or micro-controllers and, DSPs.

All the ports that currently exist for µC/OS can be easily converted to µC/OS-II in about an hour. Also, because µC/OS-II is upward compatible with µC/OS, your µC/OS applications should run on µC/OS-II with few or no changes.

Check for the availability of ports on the µC/OS-II Web site at ‘www.uCOS-II.com’.

(6)

ROMable:

µC/OS-II was designed for embedded applications. This means that if you have the proper tool chain (i.e. C compiler, assembler and linker/locator), you can embed µC/OS-II as part of a product.

Scalable:

I designed µC/OS-II so that you can use only the services that you need in your application. This means that a product can have just a few of µC/OS-II’s services while another product can have the full set of features. This allows you to reduce the amount of memory (both RAM and ROM) needed by µC/OS-II on a product per product basis. Scalability is accomplished with the use of conditional compilation. You simply specify (through #define constants) which features you need for your application/product. I did everything I could to reduce both the code and data space required by µC/OS-II.

(7)

Preemptive:

µC/OS-II is a fully -preemptive real-time kernel. This means that µC/OS-II always runs the highest priority task that is ready. Most commercial kernels are preemptive and µC/OS-II is comparable in performance with many of them.

Multi-tasking:

µC/OS-II can manage up to 64 tasks, however, the current version of the software reserves eight (8) of these tasks for system use. This leaves your application with up to 56 tasks. Each task has a unique priority assigned to it which means that µC/OS-II cannot do round robin scheduling. There are thus 64 priority levels.

Deterministic:

Execution time of all µC/OS-II functions and services are deterministic. This means that you can always know how much time µC/OS-II will take to execute a function or a service. Furthermore, except for one service, execution time of all µC/OS-II services do not depend on the number of tasks running in your application.

Task stacks:

Each task requires its own stack, however, µC/OS-II allows each task to have a different stack size. This allows you to reduce the amount of RAM needed in your application. With µC/OS-II’s stack checking feature, you can determine exactly how much stack space each task actually requires.

Services:

µC/OS-II provides a number of system services such as mailboxes, queues, semaphores, fixed-sized memory partitions, time related functions, etc.

Interrupt Management:

Interrupts can suspend the execution of a task and, if a higher priority task is awakened as a result of the interrupt, the highest priority task will run as soon as all nested interrupts complete. Interrupts can be nested up to 255 levels deep.

Robust and reliable:

µC/OS-II is based on µC/OS which has been used in hundreds of commercial applications since 1992. µC/OS-II uses the same core and most of the same functions as µC/OS yet offers more features.

(8)

Figures, Listings and Tables Convention:

You will notice that when I reference a specific element in a figure, I use the letter ‘F’ followed by the figure number.

A number in parenthesis following the figure number represents a specific element in the figure that I am trying to bring your attention to. F1-2(3) thus means look at the third item in Figure 1-2.

Listings and tables work exactly the same way except that a listing start with the letter ‘L’and a table with the letter

‘T’.

Source Code Conventions:

All µC/OS-II objects (functions, variables, #define constants and macros) start with OS indicating that they are Operating System related.

Functions are found in alphabetical order in all the source code files. This allows you to quickly locate any function.

You will find the coding style I use is very consistent. I have been adopting the K&R style for many years. However, I did add some of my own enhancements to make the code (I believe) easier to read and maintain. Indention is always 4 spaces, TABs are never used, always at least one space around an operator, comments are always to the right of code, comment blocks are used to describe functions, etc.

The following table provides the acronyms, abbreviations and mnemonics (AAMs) used in this book. I combine some of these AAMs to make up function, variable and #define names in a hierarchical way. For example, the function OSMboxCreate() reads like this: the function is part of the operating system (OS ), it is related to the mailbox services (Mbox) and the operation performed is to Create a mailbox. Also, all services that have similar operation share the same name. For e xample, OSSemCreate() and OSMboxCreate() perform the same operation but on their respective objects (i.e. semaphore and mailbox, respectively).

(9)

Acronym, Abbreviation or Mnemonic

Meaning

Addr Address

Blk Block

Chk Check

Clr Clear

Cnt Count

CPU Central Processing Unit

Ctr Counter

Ctx Context

Cur Current

Del Delete

Dly Delay

Err Error

Ext Extension

FP Floating-Point

Grp Group

HMSM Hours Minutes Seconds Milliseconds

ID Identifier

Init Initialize

Int Interrupt

ISR Interrupt Service Routine

Max Maximum

Mbox Mailbox

Mem Memory

Msg Message

N Number of

Opt Option

OS Operating System

Ovf Overflow

Prio Priority

Ptr Pointer

Q Queue

Rdy Ready

Req Request

Sched Scheduler

Sem Semaphore

Stat Status or statistic

Stk Stack

Sw Switch

Sys System

Tbl Table

TCB Task Control Block

TO Timeout

Acronyms, abbreviations and mnemonics used in this book

(10)

Chapter contents:

Chapter 1, Sample Code

This chapter is designed to allow you to quickly experiment with and use µC/OS-II. The chapter starts by showing you how to install the distribution diskette and describe the directories created. I then explain some of the coding convent ions used. Before getting into the description of the examples, I describe the code used to access some of the services provided on a PC.

Chapter 2, Real-Time Systems Concepts

Here, I introduce you to some real-time systems concepts such as foreground/background systems, critical sections, resources, multitasking, context switching, scheduling, reentrancy, task priorities, mutual exclusion, semaphores, intertask communications, interrupts and more.

Chapter 3, Kernel Structure

This chapter introduces you to µC/OS -II and its internal structure. You will learn about tasks, task states, task control blocks, how µC/OS -II implements a ready list, task scheduling, the idle task, how to determine CPU usage, how µC/OS-II handles interrupts, how to initialize and start µC/OS-II and more.

Chapter 4, Task Management

This chapter describes µC/OS-II’s services to create a task, delete a task, check the size of a task’s stack, change a task’s priority, suspend and resume a task, and get information about a task.

Chapter 5, Time Management

This chapter describes how µC/OS-II can suspend a task’s execution until some user specified time expires, how such a task can be resumed and how to get and set the current value of a 32-bit tick counter.

Chapter 6, Intertask Communication and Synchronization

This chapter describes µC/OS-II’s services to have tasks and ISRs (Interrupt Service Routines) communicate with one another and share resources. You will learn how sempahores, message mailboxes and message queues are implemented.

Chapter 7, Memory Management

This chapter describes µC/OS-II’s dynamic memory allocation feature using fixed-sized memory blocks.

Chapter 8, Porting µC/OS -II

This chapter describes in general terms what needs to be done to adapt µC/OS-II to different processor architectures.

(11)

Chapter 9, 80x86 Large Model Port

This chapter describes how µC/OS-II was ported to the Intel/AMD 80x86 processor architecture running in real-mode and for the large model. Code and data space memory usage is provided as well as execution times for each of the functions.

Chapter 10, Upgrading from µC/OS to µC/OS-II

This chapter describes how easy it is to migrate a port done for µC/OS to work with µC/OS-II.

Chapter 11, Reference Manual

This chapter describes each of the function s (i.e. services) provided by µC/OS -II from an application developer’s standpoint. Each function contains a brief description, its prototype, the name of the file where the function is found, a description of the function arguments and the return value, s pecial notes and examples.

Chapter 12, Configuration Manual

This chapter describes each of the #define constants used to configure µC/OS-II for your application. Configuring µC/OS-II allows you to use only the services required by your application. This gives you the flexibility to reduce µC/OS-II’s memory footprint (code and data space).

Appendix A, Example Source Code

Fully commented source code for the examples and PC services (see Chapter 1) is provided in this appendix and consist of 11 files.

Appendix B, µC/OS -II Microprocessor Independent Source Code

The source code for the portion of µC/OS -II that is not dependent on any specific processor is provided in this appendix and consist of 9 files

Appendix C, 80x86 Real-Mode, Large Model Source Code

The source code for the 80x86 processor dependent functions is found in this appendix and consist of three files.

Appendix D, TO and HPLISTC

Presents two DOS utilities that I use: TO and HPLISTC. TO is a utility that I use to quickly move between MS -DOS directories without having to type the CD (change directory) command. HPLISTC is a utility to print C source code in compressed mode (i.e. 17 CPI) and allows you to specify page breaks. The printout is assumed to be to a Hewlett Packard (HP) Laserjet type printer.

Appendix E, Bibliography

This section provides a bibliography of reference material that you may find useful if you are interested in getting further information about embedded real-time systems.

Appendix F, Licensing

Describes the licensing policy for distributing µC/OS-II in source and object form.

µC/OS-II Web site:

To better support you, I created the µC/OS-II WEB site (www.uCOS-II.com). You can thus obtain information about:

• News on µC/OS and µC/ OS-II,

• Upgrades,

• Bug fixes,

• Availability of ports,

• Answers to frequently asked questions (FAQs),

• Application notes,

• Books,

• Classes,

(12)

• Links to other WEB sites, and

• More.

(13)

Chapter 1.

Sample Code

This chapter provides you with three examples on how to use µC/OS-II. I decided to include this chapter early in the book to allow you to start using µC/OS-II as soon as possible. Before getting into the examples, however, I will describe some of the conventions I use throughout this book.

The sample code was compiled using the Borland International C/C++ compiler V3.1 and options were selected to generate code for an Intel/AMD 80186 processor (Large memory model). The code was actually ran and tested on a 300 MHz Intel Pentium-II based PC with can be viewed as a super fast 80186 processor (at least for my purpose). I chose a PC as my target system for a number of reasons. First and foremost, it's a lot easier to test code on a PC than on any other embedded environment (i.e., evaluation board, emulator etc.): there are no EPROMs to burn, no downloads to EPROM emulators, CPU emulators, etc. You simply compile, link, and run. Second, the 80186 object code (Real Mode, Large Model) generated using the Borland C/C++ compiler is compatible with all 80x86 derivative processors from Intel, AMD or Cyrix.

(14)

1.00 Installing µC/OS-II

R & D Publications, Inc. has included a Companion Diskette to µC/OS-II, The Real-Time Kernel. The diskette is in MS-DOS format and contains all the source code provided in this book. It is assumed that you have a DOS or Windows 95 based computer system running on an 80x86, Pentium or Pentium-II processor.

You will need less than about 5 Mbytes of free disk space to install µC/OS-II and its source files on your system.

Before starting the installation, make a backup copy of the companion diskette. To install the code provided on the companion diskette, follow these steps:

1) Load DOS (or open a DOS box in Windows 95) and specify the C: drive as the default drive.

Insert the companion diskette in drive A:

Enter A:INSTALL [drive]

Note that [drive] is an optional drive letter indicating the destination disk on which the source code provided in this book will be installed. If you do not specify a drive, the source code will be installed on the current drive.

INSTALL

is a DOS batch file called

INSTALL.BAT

and is found in the root directory of the companion diskette.

INSTALL.BAT

will create a

\SOFTWARE

directory on the specified destination drive.

INSTALL.BAT

will then change the directory to

\SOFTWARE

and copy the file

uCOS-II.EXE

from the A: drive to this directory.

INSTALL.BAT

will then execute

uCOS-II.EXE

, which will create all other directories under

\SOFTWARE

and transfer all source and executable files provided in this book. Upon completion,

INSTALL.BAT

will delete

uCOS-II.EXE

and change the directory to \

SOFTWARE\uCOS-II\EX1_x86L

where the first example code is found.

Make sure you read the

READ.ME

file on the companion diskette for last minute changes and notes.

(15)

Once

INSTALL.BAT

has completed, your destination drive will contain the following subdirectories:

\SOFTWARE

The main directory from the root where all software-related files are placed.

\SOFTWARE\BLOCKS

The main directory where all ‘Building Blocks’are located. With µC/OS-II, I included a ‘building block’that handles DOS-type compatible functions that are used by the example code.

\SOFTWARE\BLOCKS\TO

This directory contains the files for the TO utility (see Appendix E, TO). The source file is

TO.C

and is found in the

\SOFTWARE\TO\SOURCE

directory. The DOS executable file (

TO.EXE

) is found in the

\SOFTWARE\TO\EXE

directory. Note that TO requires a file called

TO.TBL

which must reside on your root directory. An example of

TO.TBL

is also found in the

\SOFTWARE\TO\EXE

directory. You will need to move

TO.TBL

to the root directory if you are to use

TO.EXE

.

\SOFTWARE\uCOS-II

The main directory where all µC/OS-II files are located.

\SOFTWARE\uCOS-II\EX1_x86L

This directory contains the source code for EXAMPLE #1 (see section 1.07, Example #1) which is intended to run under DOS (or a DOS window under Windows 95).

\SOFTWARE\uCOS-II\EX2_x86L

This directory contains the source code for EXAMPLE #2 (see section 1.08, Example #2) which is intended to run under DOS (or a DOS window under Windows 95).

\SOFTWARE\uCOS-II\EX3_x86L

This directory contains the source code for EXAMPLE #3 (see section 1.09, Example #3) which is intended to ru n under DOS (or a DOS window under Windows 95).

\SOFTWARE\uCOS-II\Ix86L

This directory contains the source code for the processor dependent code (a.k.a. the port) of µC/OS-II for an 80x86 Real-Mode, Large Model processor.

\SOFTWARE\uCOS-II\SOURCE

This directory contains the source code for processor independent portion of µC/OS-II. This code is fully portable to other processor architectures.

1.01 INCLUDES.H

You will notice that every .C file in this book contains the following declaration:

#include "includes.h"

(16)

INCLUDES.H

allows every .C file in your project to be written without concern about which header file will actually be included. In other words,

INCLUDES.H

is a Master include file. The only drawback is that

INCLUDES.H

includes header files that are not pertinent to some of the .C file being compiled. This means that each file will require extra time to compile. This inconvenience is offset by code portability.

There is an

INCLUDES.H

for every example provided in this book. In other words, you will find a

different copy of

INCLUDES.H

in

\SOFTWARE\uCOS-II\EX1_x86L

,

\SOFTWARE\uCOS-II\EX2_x86L

and

\SOFTWARE\uCOS-II\EX3_x86L

. You can certainly edit

INCLUDES.H

to add your own header files.

1.02 Compiler Independent Data Types

Because different microprocessors have different word length, the port of µC/OS-II includes a series of type definitions that ensures portability (see

\SOFTWARE\uCOS-II\Ix86L\OS_CPU.H

for the 80x86 real-mode, large model). Specifically, µC/OS-II’s code never makes use of C’s

short

,

int

and,

long

data types because they are inherently non-portable. Instead, I defined integer data types that are both portable and intuitive as shown in listing 1.1. Also, for convenience, I have included floating-point data types even though µC/OS-II doesn’t make use of floating-point.

(17)

typedef unsigned char BOOLEAN;

typedef unsigned char INT8U;

typedef signed char INT8S;

typedef unsigned int INT16U;

typedef signed int INT16S;

typedef unsigned long INT32U;

typedef signed long INT32S;

typedef float FP32;

typedef double FP64;

#define BYTE INT8S

#define UBYTE INT8U

#define WORD INT16S

#define UWORD INT16U

#define LONG INT32S

#define ULONG INT32U

Listing 1.1, Portable data types.

The

INT16U

data type, for example, always represents a 16-bit unsigned integer. µC/OS-II and your application code can now assume that the range of values for variables declared with this type is from 0 to 65535. A µC/OS-II port to a 32-bit processor could mean that an

INT16U

would be declared as an

unsigned short

instead of an

unsigned int

. Where µC/OS-II is concerned, however, it still deals with an

INT16U

. Listing 1.1 provides the declarations for the 80x86 and the Borland C/C++

compiler as an example.

For backward compatibility with µC/OS, I also defined the data types

BYTE

,

WORD

,

LONG

(and their unsigned variations). This allows you to migrate µC/OS code to µC/OS-II without changing all instances of the old data types to the new data types. I decided to make this transition and break away from the old style data types because I believe that this new scheme makes more sense and is more obvious. A

WORD

to some people may mean a 32-bit value whereas I originally intended it to mean a 16-bit value. With the new data types, there should be no more confusion.

1.03 Global Variables

Following is a technique that I use to declare global variables. As you know, a global variable needs to be allocated storage space in RAM and must be referenced by other modules using the C keyword extern.

Declarations must thus be placed in both the .C and the .H files. This duplication of declarations, however, can lead to mistakes. The technique described in this section only re quires a single declaration in the header file, but is a little tricky to understand. However, once you know how this technique works you will apply it mechanically.

In all .H files that define global variables, you will find the following declaration:

#ifdef xxx_GLOBALS

#define xxx_EXT

#else

(18)

#define xxx_EXT extern

#endif

Listing 1.2, Defining global macros.

Each variable that needs to be declared global will be prefixed with

xxx_EXT

in the .H file. ‘xxx’

represents a prefix identifying the module name. The module's .C file will contain the following declaration:

#define xxx_GLOBALS

#include "includes.h"

When the compiler processes the .C file it forces

xxx_EXT

(found in the corresponding .H file) to

"nothing" (because

XXX_GLOBALS

is defined) and thus each global variable will be allocated storage space. When the compiler processes the other .C files,

xxx_GLOBALS

will not be defined and thus

xxx_EXT

will be set to

extern

, allowing you to reference the global variable. To illustrate the concept, let's look at

uCOS_II.H

which contains the following declarations:

#ifdef OS_GLOBALS

#define OS_EXT

#else

#define OS_EXT extern

#endif

OS_EXT INT32U OSIdleCtr OS_EXT INT32U OSIdleCtrRun;

OS_EXT INT32U OSIdleCtrMax;

uCOS_II.c

contains the following declarations:

#define OS_GLOBALS

#include “includes.h”

When the compiler processes

uCOS_II.C

it makes the header file (

uCOS_II.H

) appear as shown below because

OS_EXT

is set to "nothing":

(19)

INT32U OSIdleCtr INT32U OSIdleCtrRun;

INT32U OSIdleCtrMax;

The compiler is thus told to allocate storage for these variables. When the compiler processes any other .C files, the header file (

uCOS_II.H

) looks as shown below because

OS_GLOBALS

is not defined and thus

OS_EXT

is set to extern.

extern INT32U OSIdleCtr extern INT32U OSIdleCtrRun;

extern INT32U OSIdleCtrMax;

In this case, no storage is allocated and any .C file can access these variables. The nice thing about this technique is that the decla ration for the variables is done in only one file, the .H file.

1.04 OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()

Throughout the source code provided in this book, you will see calls to the following macros:

OS_ENTER_CRITICAL()

and

OS_EXIT_CRITICAL()

.

OS_ENTER_CRITICAL()

is a macro that disables interrupts and

OS_EXIT_CRITICAL()

is a macro that enables interrupts.

Disabling and enabling interrupts is done to protect critical sections of code. These macros are obviously processor specific and are different for each processor. These macros are found in

OS_CPU.H

. Listing 1.3 shows the declarations of these macros for the 80x86 processor. Section 9.03.02 discusses why there are two ways of declaring these macros.

#define OS_CRITICAL_METHOD 2

#if OS_CRITICAL_METHOD == 1

#define OS_ENTER_CRITICAL() asm CLI

#define OS_EXIT_CRITICAL() asm STI

#endif

#if OS_CRITICAL_METHOD == 2

#define OS_ENTER_CRITICAL() asm {PUSHF; CLI}

#define OS_EXIT_CRITICAL() asm POPF

#endif

Listing 1.3, Macros to access critical sections.

Your application code can make use of these macros as long as you realize that they are used to disable and enable interrupts. Disabling interrupts obviously affect interrupt latency so be careful. You can also protect critical sections using semaphores.

1.05 PC Based Services

(20)

The files

PC.C

and

PC.H

(in the

\SOFTWARE\BLOCKS\PC\SOURCE

directory) contain PC compatible services that I used throughout the examples. Unlike the first version of µC/OS-II (i.e. µC/OS), I decided to encapsulate these functions (as they should have been) to avoid redefining them in every example and also, to allow you to easily adapt the code to a different compiler.

PC.C

basically contains three types of services: character based display, elapsed time measurement and, miscellaneous. All functions start with the prefix

PC_

.

1.05.01 PC Based Services, Character Based Display

The display functions perform direct writes to video RAM for performance reasons. On a VGA monitor, video memory starts at absolute memory location 0x000B8000 (or using a segment:offset notation, B800:0000). You can use this code on a monochrome monitor by changing the

#define

constant

DISP_BASE

from 0xB800 to 0xB000.

The display functions in

PC.C

are used to write ASCII (and special) characters anywhere on the screen using X and Y coordinates. A PC’s display can hold up to 2000 characters organized as 25 rows (i.e. Y) by 80 columns (i.e. X). Each character requires two bytes to display. The first byte is the character that you want to display while the second byte is an attribute that determines the foreground/background color combination of the character. The foreground color is specified in the lower 4 bits of the attribute while the background color appears in bits 4 to 6. Finally, the most-significant bit determines whether the character will blink (when 1) or not (when 0). You should use the

#define

constants declared in

PC.C

(FGND means foreground and BGND is background).

PC.C

contains the following four functions:

PC_DispClrScr() To clear the screen

PC_DispClrLine() To clear a single row (or line)

PC_DispChar() To display a single ASCII character anywhere on the screen

PC_DispStr() To display an ASCII string anywhere on the screen

(21)

1.05.02 PC Based Services, Elapsed Time Measurement

The elapsed time measurement functions are used to determine how much time a function takes to execute.

Time measurement is performed by using the PC’s 82C54 timer #2. You make time measurement by wrapping the code to measure by the two functions

PC_ElapsedStart()

and

PC_ElapsedStop()

. However, before you can use these two functions, you need to call the function

PC_ElapsedInit()

.

PC_ElapsedInit()

basically computes the overhead associated with the other two functio ns. This way, the execution time returned by

PC_ElapsedStop()

consist exclusively of the code you are measuring. Note that none of these functions are reentrant and thus, you must be careful that you do not invoke them from multiple tasks at the same time. The example in listing 1.4 shows how you could measure the execution time of

PC_DispChar()

. Note that time is in microseconds (µS).

INT16U time;

PC_ElapsedInit();

. .

PC_ElapsedStart();

PC_DispChar(40, 24, ‘A’, DISP_FGND_WHITE);

time = PC_ElapsedStop();

Listing 1.4, Measuring code execution time.

1.05.03 PC Based Services, Miscellaneous

A µC/OS-II application looks just like any other DOS application. In other words, you compile and link your code just as if you would do a single threaded application running under DOS. The .EXE file that you create is loaded and executed by DOS and execution of your application starts from

main()

. Because µC/OS-II performs multitasking and needs to create a stack for each task, the single threaded DOS environment must be stored in case your application wishes to quit µC/OS-II, and return to DOS. Saving the DOS environment is done by calling

PC_DOSSaveReturn()

. When your application needs to return to DOS (and exit µC/OS-II), you simply call

PC_DOSReturn()

.

PC.C

makes use of the ANSI C

setjmp()

and

longjmp()

functions to save and restore the DOS environment, respectively. These functions are provided by the Borland C/C++ compiler library and should be available on most other compilers.

You should note that a crashed application or invoking

exit(0)

without using

PC_DOSReturn()

can leave DOS is a corrupted state. This may lead to a crash of DOS, or the DOS window within Windows 95.

PC_GetDateTime()

is a function that obtains the PC’s current date and time, and formats this information into an ASCII string. The format is “MM-DD-YY HH:MM:SS” and you will need at least 19 characters (including the NUL character) to hold this string.

PC_GetDateTime()

uses the Borland

(22)

C/C++ library functions

gettime()

and

getdate()

which should have their equivalent on other DOS compilers.

PC_GetKey()

is a function that checks to see if a key was pressed and if so, obtains that key, and returns it to the caller.

PC_GetKey()

uses the Borland C/C++ library functions

kbhit()

and

getch()

which again, have their equivalent on other DOS compilers.

PC_SetTickRate()

allows you to change the tick rate for µC/OS-II by specifying the desired frequency. Under DOS, a tick occurs 18.20648 times per second or, every 54.925 mS. This is because the 82C54 chip used didn’t get its counter initialized and the default value of 65535 takes effect. Had the chip been initialized with a divide by 59659, the tick rate would have been a very nice 20.000 Hz! I decided to change the tick rate to something more ‘exciting’and thus, decided to use about 200 Hz (actually 199.9966).

You will note that the function

OSTickISR()

found in

OS_CPU_A.ASM

contains code to call the DOS tick handler one time out of 11. This is done to ensure that some of the housekeeping needed in DOS is maintained. You would not need to do this if you were to set the tick rate to 20 Hz. Before returning to DOS,

PC_SetTickRate()

is called by specifying 18 as the desired frequency.

PC_SetTickRate()

will know that you actually mean 18.2 Hz and will correctly set the 82C54.

The last two functions in

PC.C

are used to get and set an interrupt vector. Again, I used Borland C/C++

library functions do accomplish this but, the

PC_VectGet()

and

PC_VectSet()

can easily be changed to accommo date a different compiler.

1.06 µC/OS-II Examples

The examples provided in this chapter was compiled using the Borland C/C++ V3.1 compiler in a DOS box on a Windows 95 platform. The executable code is found in the OBJ subdirectory of each example’s directory. The code was actually compiled under the Borland IDE (Integrated Development Environment) with the following options:

(23)

Compiler:

Code generation:

Model : Large

Options : Treat enums as ints Assume SS Equals DS : Default for memory model Advanced code generation:

Floating point : Emulation

Instruction set : 80186 Options : Generate underbars

Debug info in OBJs Fast floating point Optimizations:

Optimizations:

Global register allocation Invariant code motion Induction variables Loop optimization Suppress redundant loads Copy propagation Dead code elimination Jump optimization Inline intrinsic functions Register variables:

Automatic Common subexpressions:

Optimize globally Optimize for:

Speed

It is assumed that the Borland C/C++ compiler is installed in the

C:\CPP

directory. If your compiler is located in a different directory, you will need to change the path in the Options/Directories menu of the IDE.

µC/OS-II is a scalable operating system which means that the code size of µC/OS-II can be reduced if you are not using all of its services. Code reduction is done by setting the

#defines OS_???_EN

to 0 in

OS_CFG.H

. You do this to disable code generation for the services that you will not be using. The examples in this chapter makes use of this feature and thus, each example declares their

OS_???_EN

appropriately.

(24)

1.07 Example #1

The first example is found in

\SOFTWARE\uCOS-II\EX1_x86L

and basically consists of 13 tasks (including µC/OS-II’s idle task). µC/OS-II creates two ‘internal’tasks: the idle task and a task that determines CPU usage. Example #1 creates 11 other tasks. The

TaskStart()

task is created by

main()

and its function is to create the other tasks and display the following statistics on the screen:

1) the number of task switches in one second, 2) the CPU usage in %,

3) the number of context switches, 4) the current date and time, and 5) µC/OS-II’s version.

The

TaskStart()

task also checks to see if you pressed the ESCAPE key indicating your desire to exit the example and return to DOS.

The other 10 tasks are based on the same code, i.e. the function

Task()

. Each of the 10 tasks displays a number (each task has its own number from 0 to 9) at random locations on the screen.

1.07.01 Example #1, main()

Example #1 does basically the same thing as the first example provided in the first edition of µC/OS, however, I cleaned up some of the code and output to the screen is in color. Also, I dec ided to use the old data types (i.e.

UBYTE

,

UWORD

etc.) to show that µC/OS-II is backward compatible.

A µC/OS-II application looks just like any other DOS application. You compile and link your code just as if you would do a single threaded application running under DOS. The .EXE file that you create is loaded and executed by DOS, and execution of your application starts from

main()

.

(25)

main()

starts by clearing the screen to ensure we don’t have any characters left over from the previous DOS session L1.5(1). Note that I specified to use white letters on a black background. Since the screen will be cleared, I could have simply specified to use a black background and not specify a foreground. If I did this, and you decided to return to DOS then you would not see anything on the screen! It’s always better to specify a visible foreground just for this reason.

void main (void) {

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); (1) OSInit(); (2) PC_DOSSaveReturn(); (3) PC_VectSet(uCOS, OSCtxSw); (4) RandomSem = OSSemCreate(1); (5) OSTaskCreate(TaskStart, (6) (void *)0,

(void *)&TaskStartStk[TASK_STK_SIZE-1], 0);

OSStart(); (7) }

Listing 1.5, main()

A requirement of µC/OS-II is that you call

OSInit()

L1.5(2) before you invoke any of its other services.

OSInit()

creates two tasks: an idle task which executes when no other task is ready-to-run and, a statistic task which computes CPU usage.

The current DOS environment is then saved by calling

PC_DOSSaveReturn()

L1.5(3). This allows us to return to DOS as if we had never started µC/OS-II. A lot happens in

PC_DOSSaveReturn()

so you may need to look at the code in listing 1.6 to follow along.

PC_DOSSaveReturn()

starts by setting the flag

PC_ExitFlag

to

FALSE

L1.6(1) indicating that we are not returning to DOS. Then,

PC_DOSSaveReturn()

initializes

OSTickDOSCtr

to 1 L1.6(2) because this variable will be decremented in

OSTickISR()

. A value of 0 would have caused this value to wrap around to 255 when decremented by

OSTickISR()

.

PC_DOSSaveReturn()

then saves DOS’s tick handler in a free vector table L1.6(3)-(4) entry so it can be called by µC/OS-II’s tick handler. Next,

PC_DOSSaveReturn()

calls

setjmp()

L1.6(5), which captures the state of the pro cessor (i.e., the contents of all its registers) into a structure called

PC_JumpBuf

. Capturing the processor's context will allow us to return to

PC_DOSSaveReturn()

and execute the code immediately following the call to

setjmp()

. Because

PC_ExitFlag

was initialized to

FALSE

L1.6(1),

PC_DOSSaveReturn()

skips the code in the if statement (i.e. L1.6(6) -(9)) and returns to the caller (i.e.

main()

). When you want to return to DOS, all you have to do is call

PC_DOSReturn()

(see listing 1.7) which sets

PC_ExitFlag

to

TRUE

L1.7(1) and execute a

longjmp()

L1.7(2). This brings the processor back in

PC_DOSSaveReturn()

(just after the call to

setjmp()

) L1.6(5).

This time, however,

PC_ExitFlag

is

TRUE

and the code following the if statement is executed.

PC_DOSSaveReturn()

changes the tick rate back to 18.2 Hz L1.6(6), restores the PC’s tick ISR

(26)

handler L1.6(7), clears the screen L1.6(8) and returns to the DOS prompt through the

exit(0)

function L1.6(9).

void PC_DOSSaveReturn (void) {

PC_ExitFlag = FALSE; (1) OSTickDOSCtr = 8; (2) PC_TickISR = PC_VectGet(VECT_TICK); (3)

OS_ENTER_CRITICAL();

PC_VectSet(VECT_DOS_CHAIN, PC_TickISR); (4) OS_EXIT_CRITICAL();

setjmp(PC_JumpBuf); (5) if (PC_ExitFlag == TRUE) { OS_ENTER_CRITICAL();

PC_SetTickRate(18); (6) PC_VectSet(VECT_TICK, PC_TickISR); (7) OS_EXIT_CRITICAL();

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); (8) exit(0); (9) }

}

Listing 1.6, Saving the DOS environment.

void PC_DOSReturn (void) {

PC_ExitFlag = TRUE; (1) longjmp(PC_JumpBuf, 1); (2) }

Listing 1.7, Setting up to return to DOS.

Now we can go back to

main()

in listing 1.5.

main()

then calls

PC_VectSet()

L1.5(4) to install µC/OS-II’s context switch handler. Task level context switching is done by issuing an 80x86

INT

instruction to this vector location. I decided to use vector 0x80 (i.e. 128) because it’s not used by either DOS or the BIOS.

A binary semaphore is then created L1.5(5) to guard access to the random number generator provided by the Borland C/C++ library. I decided to use a semaphore because I didn’t know whether or not this function was reentrant. I assumed it was not. Because I initialized the semaphore to 1, I am indicating that only one task can access the random number generator at any time.

Before starting multitasking, I create one task L1.5(6) called

TaskStart()

. It is very important that you create at least one task before starting multitasking through

OSStart()

L1.5(7). Failure to do this will certainly make your application crash. In fact, you may always want to only create a single task if you

(27)

are planning on using the CPU usage statistic task. µC/OS-II’s statistic task assumes that no other task is running for a whole second. If, however, you need to create additional tasks before starting multitasking, you must ensure that your task code will monitor the global variable

OSStatRdy

and delay execution (i.e.

call

OSTimeDly()

) until this variable becomes

TRUE

. This indicates that µC/OS-II has collected its data for CPU usage statistics.

1.07.02 Example #1, TaskStart()

A major portion of the work in example #1 is done by

TaskStart()

. The pseudo-code for this function is shown in listing 1.8. The task starts by displaying a banner on top of the screen identifying this as example

#1 L1.8(1). Next, we disable interrupts to change the tick ISR (Interrupt Service Routine) vector so that it now points to µC/OS-II’s tick handler L1.8(2) and, change the tick rate from the default DOS 18.2 Hz to 200 Hz L1.8(3). We sure don’t want to be interrupted while in the process of changing an interrupt vector! Note that

main()

purposely didn't set the interrupt vector to µC/OS-II's tick handler because you don't want a tick interrupt to occur before the operating system is fully initialized and running. If you run code in an embedded application, you should always enable the ticker (as I have done here) from within the first task.

(28)

void TaskStart (void *data) {

Prevent compiler warning by assigning ‘data’ to itself;

Display banner identifying this as EXAMPLE #1; (1)

OS_ENTER_CRITICAL();

PC_VectSet(0x08, OSTickISR); (2) PC_SetTickRate(200); (3) OS_EXIT_CRITICAL();

Initialize the statistic task by calling ‘OSStatInit()’; (4)

Create 10 identical tasks; (5) for (;;) {

Display the number of tasks created;

Display the % of CPU used;

Display the number of task switches in 1 second;

Display uC/OS-II’s version number if (key was pressed) {

if (key pressed was the ESCAPE key) { PC_DOSReturn();

} }

Delay for 1 Second;

} }

Listing 1.8, Task that creates the other tasks.

Before we create any other tasks, we need to determine how fast you particular PC is. This is done by calling

OSStatInit()

L1.8(4).

OSStatInit()

is shown in listing 1.9 and starts by delaying itself for two clock ticks so that it can be synchro nized to the tick interrupt L1.9(1). Because of this,

OSStatInit()

MUST occur after the ticker has been installed otherwise, your application will crash!

When µC/OS-II returns control to

OSStatInit()

, a 32-bit counter called

OSIdleCtr

is cleared L1.9(2) and another delay is initiated, which again suspends

OSStatInit()

. At this point, µC/OS-II doesn't have anything else to execute and thus decides to run the Idle Task (internal to µC/OS-II). The idle task is an infinite loop that increments

OSIdleCtr

. The idle task gets to increment this counter for one full second L1.9(3). After one second, µC/OS-II resumes

OSStatInit()

, which saves

OSIdleCtr

in a variable called

OSIdleCtrMax

L1.9(4).

OSIdleCtrMax

now contains the largest value that

OSIdleCtr

can ever reach. When you start adding application code, the idle task will get less of the processor's time and thus,

OSIdleCtr

will not be allowed to count as high (assuming we will reset

OSIdleCtr

every second). CPU utilization is computed by a task provided b y µC/OS-II called

OSStatTask()

which executes every second.

void OSStatInit (void) {

OSTimeDly(2); (1)

(29)

OS_ENTER_CRITICAL();

OSIdleCtr = 0L; (2) OS_EXIT_CRITICAL();

OSTimeDly(OS_TICKS_PER_SEC); (3) OS_ENTER_CRITICAL();

OSIdleCtrMax = OSIdleCtr; (4) OSStatRdy = TRUE; (5) OS_EXIT_CRITICAL();

}

Listing 1.9, Determining the PC’ s speed.

1.07.03 Example #1, TaskN()

OSStatInit()

returns back to

TaskStart()

and we can now create 10 identical tasks (all running the same code) L1.8(5).

TaskStart()

will create all the tasks and no context switch will occur because

TaskStart()

has a priority of 0 (i.e. the highest priority). When all the tasks are created,

TaskStart()

enters the infinite loop portion of the task and continuously displays statistics on the screen, checks to see if the ESCAPE key was pressed and then delay for one second before starting the loop again. If you press the escape key,

TaskStart()

calls

PC_DOSReturn()

and we gracefully return back to the DOS prompt.

The task code is shown in listing 1.10. When the task gets to execute, it trie s to acquire a semaphore L1.10(1) so that we can call the Borland C/C++ library function

random()

L1.10(2). I assumed here that the random function is non-reentrant so, each of the 10 tasks must have exclusive access to this code in order to proceed. We release the semaphore when both X and Y coordinates are obtained L1.10(3). The task displays a number (between ‘0’and ‘9’) which is passed to the task when it is created L1.10(4). Finally, each task delays itself for one tick L1.10(5) and thus, each tas k will execute 200 times per second! With the 10 task this means that µC/OS-II will context switch between these tasks 2000 per second.

(30)

void Task (void *data) {

UBYTE x;

UBYTE y;

UBYTE err;

for (;;) {

OSSemPend(RandomSem, 0, &err); (1) x = random(80); (2) y = random(16);

OSSemPost(RandomSem); (3) PC_DispChar(x, y + 5, *(char *)data, DISP_FGND_LIGHT_GRAY); (4) OSTimeDly(1); (5) }

}

Listing 1.10,

Task that displays a number at random locations on the screen.

1.08 Example #2

Example #2 makes use of the extended task create function and µC/OS-II’s stack checking feature. Stack checking is useful when you don’t actually know ahead of time how much stack space you need to allocate for each task. In this case, you allocate much more stack space than you think you need and you let µC/OS-II tell you exactly how much stack space is actually used up. You obviously need to run the application long enough and under your worst case conditions to get proper numbers. Your final stack size should accommodate for system expansion so make sure you allocate between 10 and 25% more. In safety critical applications, however, you may even want to consider 100% more. What you should get from stack checking is a ‘ballpark’figure; you are not looking for an exact stack usage.

µC/OS-II’s stack checking function assumes that the stack is initially filled with zeros when the task is created. You accomplish this by telling

OSTaskCreateExt()

to clear the stack upon task creation (i.e. you OR both

OS_TASK_OPT_STK_CHK

and

OS_TASK_OPT_STK_CLR

for the

opt

argument). If you intend to create and delete tasks, you should set these options so that a new stack is cleared every time the task is created. You should note that having

OSTaskCreateExt()

clear the stack increases execution overhead which obviously depends on the stack size. µC/OS -II scans the stack starting at the bottom until it finds a non-zero entry (see figure 1-1). As the stack is scanned, µC/OS-II increments a counter that indicates how many entries are free (Stack Free).

(31)

0x0000 0x0000 0x0000 0x0000

0x0000 0x0000 0x0000

Stack Used Stack Free Bottom-Of-Stack

Top-Of-Stack

Stack Size

Stack Growth /OS-II 'Scans' stack

Figure 1-1 µC/OS-II Stack checking

The second example is found in

\SOFTWARE\uCOS-II\EX2_x86L

and consists of a total of 9 tasks. Again, µC/OS-II creates two ‘internal’tasks: the idle task and the task that determines CPU usage.

EX2L.C

creates the other 7 tasks. As with example #1,

TaskStart()

is created by

main()

and its function is to create the other tasks and display the following statistics on the screen:

1) the number of task switches in one second, 2) the CPU usage in %,

3) the number of context switches, 4) the current date and time, and 5) µC/OS-II’s version.

(32)

1.08.01 Example #2, main()

main()

looks just like the code for example #1 (see listing 1.11) except for two small differences. First,

main()

calls

PC_ElapsedInit()

L1.11(1) to in itialize the elapsed time measurement function which will be used to measure the execution time of

OSTaskStkChk().

Second, all tasks are created using the extended task create function instead of

OSTaskCreate()

L1.11(2). This allows us, among other things, to perform stack checking on each task. In addition to the same four arguments needed by

OSTaskCreate()

,

OSTaskCreateExt()

requires five additional arguments: a task ID, a pointer to the bottom of the stack, the stack size (in number of elements), a p ointer to a user supplied Task Control Block (TCB) extension, and a variable used to specify options to the task. One of the options is used to tell µC/OS-II that stack checking is allowed on the created task. Example #2 doesn’t make use of the TCB (Task Control Block) extension pointer.

void main (void) {

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);

OSInit();

PC_DOSSaveReturn();

PC_VectSet(uCOS, OSCtxSw);

PC_ElapsedInit(); (1) OSTaskCreateExt(TaskStart, (2) (void *)0,

&TaskStartStk[TASK_STK_SIZE-1], TASK_START_PRIO,

TASK_START_ID, &TaskStartStk[0], TASK_STK_SIZE, (void *)0,

OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

OSStart();

}

Listing 1.11, main() for example #2.

(33)

1.08.02 Example #2, TaskStart()

Listing 1.12 shows the pseudo code for

TaskStart()

. The first five operations are similar to those found in example #1.

TaskStart()

creates two mailboxes that will be used by Task #4 and Task #5 L1.12(1). A task that will display the current date and time is created as well as five application tasks L1.12(20).

void TaskStart (void *data) {

Prevent compiler warning by assigning ‘data’ to itself;

Display a banner and non-changing text;

Install uC/OS-II’s tick handler;

Change the tick rate to 200 Hz;

Initialize the statistics task;

Create 2 mailboxes which are used by Task #4 and #5; (1) Create a task that will display the date and time on the screen; (2) Create 5 application tasks;

for (;;) {

Display #tasks running;

Display CPU usage in %;

Display #context switches per seconds;

Clear the context switch counter;

Display uC/OS-II’s version;

if (Key was pressed) {

if (Key pressed was the ESCAPE key) { Return to DOS;

} }

Delay for 1 second;

} }

Listing 1.12, Pseudo-code for TaskStart().

1.08.03 Example #2, TaskN()

The code for

Task1()

checks the size of the stack for each of the seven application tasks. The execution time of

OSTaskStkChk()

is measured L1.13(1)-(2) and displayed along with the stack size information. Note that all stack size data are displayed in number of bytes. This task executes 10 times per second L1.13(3).

參考文獻

相關文件

* All rights reserved, Tei-Wei Kuo, National Taiwan University,

OSEventCnt OSEventType OSEventGrp OSTCBStkPtr OSTCBExtPtr OSTCBStkBottom OSTCBStkSize OSTCBId OSTCBNext OSTCBPrev OSTCBEventPtr OSTCBMsg OSTCBDly OSTCBStat OSTCBPrio

jobs

Elements of Computing Systems, Nisan & Schocken, MIT Press, www.nand2tetris.org , Chapter 1: Compiler II: Code Generation slide

• The first module, written in assembly language, contains the external procedure. • The second module contains the C/C++ code that starts and ends

(A) The PC has the TCP/IP protocol stack correctly installed (B) The PC has connectivity with a local host (C) The Pc has a default gateway correctly configured (D) The Pc

An alternative way to proceed would be to directly estimate that one of the unknown constants C 1 :::C 4 which is appropriate to the context (e.g. C 1 if we were using the excess

 When citing a foreword/introduction/preface/afterword, begin the citation with the name of the person who wrote it, then the word “Foreword” (or whatever it is), without