• 沒有找到結果。

OS_CPU.H

在文檔中 µC/OS-II Goals Preface (頁 179-183)

Software Hardware

8.03 OS_CPU.H

OS_CPU.H

contains processor a nd implementation specific

#defines

constants, macros, and

typedefs

. The general layout of

OS_CPU.H

is shown in listing 8.1:

#ifdef OS_CPU_GLOBALS

#define OS_CPU_EXT

#else

#define OS_CPU_EXT extern

#endif

/*

*********************************************************************************************************

* DATA TYPES

* (Compiler Specific)

*********************************************************************************************************

*/

typedef unsigned char BOOLEAN;

typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ (1) typedef signed char INT8S; /* Signed 8 bit quantity */

typedef unsigned int INT16U; /* Unsigned 16 bit quantity */

typedef signed int INT16S; /* Signed 16 bit quantity */

typedef unsigned long INT32U; /* Unsigned 32 bit quantity */

typedef signed long INT32S; /* Signed 32 bit quantity */

typedef float FP32; /* Single precision floating point */ (2) typedef double FP64; /* Double precision floating point */

typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */

/*

*********************************************************************************************************

* Processor Specifics

*********************************************************************************************************

*/

#define OS_ENTER_CRITICAL() ??? /* Disable interrupts */ (3)

#define OS_EXIT_CRITICAL() ??? /* Enable interrupts */

#define OS_STK_GROWTH 1 /* Define stack growth: 1 = Down, 0 = Up */ (4)

#define OS_TASK_SW() ??? (5)

Listing 8.1, Contents of OS_CPU.H

8.03.01 OS_CPU.H, Compiler Specific Data Types

Because different microprocessors have different word length, the port of µC/OS-II includes a series of type definitions that ensures portability. 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 L8.1(1). Also, for convenience, I have included floating-point data types L8.1(2) even though µC/OS-II doesn’t make use of floating-point.

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

is actually declared as an

unsigned short

instead of an

unsigned int

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

INT16U

.

You must tell µC/OS-II the data type of a task’s stack. This is done by declaring the proper C data type for

OS_STK

.

If stack elements on your processor are 32-bit and your compiler documentation specify that an

int

is 32-bit then, you would declare

OS_STK

as being of type

unsigned int

. All task stacks MUST be declared using

OS_STK

as its data type.

All you have to do is to consult the compiler’s manual and find the standard C data types that corresponds to the types expected by µC/OS-II.

8.03.02 OS_CPU.H, OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()

µC/OS-II like all real-time kernels need to disable interrupts in order to access critical sections of code, and re-enable interrupts when done. This allows µC/OS-II to protect critical code from being entered simultaneously from either multiple tasks or Interrupt Service Routines (ISRs). The interrupt disable time is one of the most important specification that a real-time kernel vendor can provide because it affects the responsiveness of your system to real-time events.

µC/OS-II tries to keep the interrupt disable time to a minimum but, with µC/OS-II, interrupt disable time is largely dependent on the processor architecture, and the quality of the code generated by the compiler. Every processor generally provide instructions to disable/enable interrupts a nd your C compiler must have a mechanism to perform these operations directly from C. Some compilers will allow you to insert in-line assembly language statements in your C source code. This makes it quite easy to insert processor instructions to enable and disable interrupts. Other compilers will actually contain language extensions to enable and disable interrupts directly from C. To hide the implementation method chosen by the compiler manufacturer, µC/OS-II defines two macros to disable and enable interrupts:

OS_ENTER_CRITICAL()

and

OS_EXIT_CRITICAL()

, respectively L8.1(3).

µC/OS-II’s critical sections are wrapped with

OS_ENTER_CRITICAL()

and

OS_EXIT_CRITICAL()

as shown below:

µC/OS-II Service Function {

OS_ENTER_CRITICAL();

/* µC/OS-II critical code section */

OS_EXIT_CRITICAL();

}

Method #1:

The first and simplest way to implement these two macros is to invoke the processor instruction to disable interrupts for

OS_ENTER_CRITICAL()

and the enable interrupts instruction for

OS_EXIT_CRITICAL()

. There is, however, a little problem with this scenario. If you called the µC/OS-II function with interrupts disabled then, upon return from µC/OS-II, interrupts would be enabled! If you had interrupts disabled, you may have wanted them to be dis abled upon return from the µC/OS-II function. In this case, the above implementation would not be adequate.

Method #2:

The second way to implement

OS_ENTER_CRITICAL()

is to save the interrupt disable status onto the stack and then, disable interrupts.

OS_EXIT_CRITICAL()

would simply be implemented by restoring the interrupt status from the stack. Using this scheme, if you called a µC/OS-II service with either interrupts enabled or disabled then, the status would be preserved across the call. If you call a µC/OS-II service with interrupts disabled, you are potentially extending the interrupt latency of your application. Your application can use

OS_ENTER_CRITICAL()

and

OS_EXIT_CRITICAL()

to also protect critical sections of code. Be careful, however, be cause your application will ‘crash’if you have interrupts disabled before calling a service such as

OSTimeDly()

. This will happen because the task will be suspended until time expires but, because interrupts are disabled, you would never service the tick interrupt! Obviously, all the PEND calls are also subject to this problem so, be careful. As a general rule, you should always call µC/OS-II services with interrupts enabled!

The question is thus, which one is better? Well, that all depends on what yo u are willing to sacrifice. If you don’t care in your application whether interrupts are enabled after calling a µC/OS-II service then, you should opt for the first method because of performance. If you want to preserve the interrupt disable status across µC/OS-II service calls then obviously the second method is for you.

Just to give you an example, disabling interrupts on an Intel 80186 is done by executing the

STI

instructions and enabling interrupts is done by executing the

CLI

instruction. You can thus implement the macros as follows:

#define OS_ENTER_CRITICAL() asm CLI

#define OS_EXIT_CRITICAL() asm STI

Both the

CLI

and

STI

instructions execute in less that 2 clock cycles each on this processor (i.e. total of 4 cycles). To preserve the interrupt status you would need to implement the macros as follows:

#define OS_ENTER_CRITICAL() asm PUSHF; CLI

#define OS_EXIT_CRITICAL() asm POPF

In this case,

OS_ENTER_CRITICAL()

would consume 12 clock cycles while

OS_EXIT_CRITICAL()

would use up another 8 clock cycles (i.e. a total of 20 cycles). Preserving the state of the interrupt disable status would thus take 16 clock cycles longer than simply disabling/enabling interrupts (at least on the 80186). Obviously, if you have a faster processor s uch as an Intel Pentium-II then, the difference would be minimal.

8.03.03 OS_CPU.H, OS_STK_GROWTH

The stack on most microprocessors and microcontrollers grows from high-memory to low-memory. There are, however, some processors that work the other way around. µC/OS-II has been designed to be able to handle either flavor. This is accomplished by specifying to µC/OS-II which way the stack grows through the configuration constant

OS_STK_GROWTH

L8.1(4) as shown below:

Set

OS_STK_GROWTH

to 0 for Low to High memory stack growth.

Set

OS_STK_GROWTH

to 1 for High to Low memory stack growth.

8.03.04 OS_CPU.H, OS_TASK_SW()

OS_TASK_SW()

L8.1(5) is a macro that is invoked when µC/OS-II switches from a low-priority task to the highest-priority task.

OS_TASK_SW()

is always called from task level code. Another mechanism,

OSIntExit()

, is used to perform a context switch when an ISR makes a higher priority task ready for execution.

A context switch simply consist of saving the processor registers on the stack of the task being suspended, and restoring the registers of the higher-priority task from its stack.

In µC/OS-II, the stack frame for a ready task always looks as if an interrupt has just occurred and all processor registers were saved onto it. In other words , all that µC/OS-II has to do to run a ready task is to restore all processor registers from the task’s stack and execute a return from interrupt. To switch context, you should implement

OS_TASK_SW()

so

that you simulate an interrupt. Most processors provide either software interrupt or

TRAP

instructions to accomplish this. The ISR or trap handler (also called the ‘exception handler’) MUST vector to the assembly language function

OSCtxSw()

(see section 8.04.02).

For example, a port for an Intel or AMD 80x86 processor would use an

INT

instruction. The interrupt handler would need to vector to OSCtxSw(). A port for the Motorola 68HC11 processor would most likely use the

SWI

instruction.

Again, the

SWI

handler would be

OSCtxSw()

. Finally, a port for a Motorola 680x0/CPU32 processor would probably use one of the 16

TRAP

instructions. Of course, the selected

TRAP

handler would be none other than

OSCtxSw()

.

There are some processors like the Zilog Z80 that do not provide a software interrupt mechanism. In this case, you would need to simulate the stack frame as closely to an interrupt stack frame as you can. In this case,

OS_TASK_SW()

would simply call

OSCtxSw()

instead of vector to it. The Z80 is a processor that has been ported to µC/OS and thus would be portable to µC/OS-II.

8.04 OS_CPU_A.ASM

A µC/OS-II port requires that you write four fairly simple assembly language functions:

OSStartHighRdy() OSCtxSw()

OSIntCtxSw()

在文檔中 µC/OS-II Goals Preface (頁 179-183)