⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 read.me

📁 基于80251的UCOS II源代码,仅供参考和学习.
💻 ME
📖 第 1 页 / 共 2 页
字号:
section of code.  The macros OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() are used to disable
and enable interrupts, respectively.  Macros are used because only a single CPU instruction is needed to
accomplish each operation.  On the MCS251, interrupts are disabled by setting the EA bit in the Special
Function Registers (SFR).  Interrupts are re-enabled by clearing the EA bit.


10.05 MCS251.H, OS_TASK_SW()
When uC/OS decides to switch from a low priority task to a higher priority task it performs a context
switch.  For the MCS251, OS_TASK_SW() is a macro which calls the function OSCtxSw() (see
MCS251.C).  The context switch simply consist of saving the current register contents of the task to
'switch out' onto its stack and then, load the registers from the stack of the task to 'switch in'.


10.05 MCS251.H, OS_FAR
uC/OS was originally designed and tested on an IBM-PC/AT compatible personal computer (PC).  PCs
use the Intel 80x86 family of CPUs.  Programs written for Real Mode on an Intel 80x86 CPU are
segmented.  Each segment cannot access more than 64KBytes of memory at any given time.
Functions in the current 64K segment are said to be near while functions outside the current segment are
said to be far.  Because the MCS251 port is not concerned with near and far segments, the #define
OS_FAR is declared to 'nothing' in MCS251.H.


11.00 MCS251.C
MCS251.C contains the executable portion of the MCS-251 port.  This file contains the functions
OSTaskCreate(), OSStartHighRdy(), OSCtxSw(), OSIntCtxSw() and OSTickISR().  These
functions are the processor specific functions required by uC/OS.


11.01 MCS251.C, OSTaskCreate()
A task is a simple program that thinks it has the CPU all to itself.  uC/OS can manage up to 63 tasks.
Each task is assigned a priority, its own set of CPU registers and its own stack area.  The function
prototype for OSTaskCreate() is:

OSTaskCreate(void (OS_FAR *)task(void *pd), void *pdata, void *pstk, UBYTE prio);

To manage the task, uC/OS needs to know the address of the code to run (task), a pointer to an
argument that will be passed to the task when the task first starts executing (pdata), a pointer to the
task's top-of-stack (pstk) and finally, the task priority (prio).  A task is an infinite loop and, for the
MCS-251, a task MUST be written as follows:

void OS_FAR UserTask(void *pdata)
{
    pdata = pdata;           /* Prevent compiler warning                 */
    OS_EXIT_CRITICAL();      /* Enable interrupts                        */
    while (TRUE) {
        /* User code goes here ...!                                      */

        /* You MUST make a service call to uC/OS to:                     */
        /* ... a) delay for 'n' ticks                                    */
        /* ... b) wait on a semaphore                                    */
        /* ... c) wait for a message from a task or ISR                  */
        /* ... d) suspend this task                                      */
    }
}

When your task first gets a chance to execute it receives an argument (pdata).  This argument can be
used to supply information to your task.  Because pdata is a pointer to a void type, it can point to
anything.  You can thus pass a pointer to a variable, an array or a data structure.  This is similar to the
argc and argv[] arguments that are passed to main().

You will notice that a task is an infinite loop.  To prevent the task from using the processor all the time,
your task needs to make a call to one of the functions supplied by uC/OS.  Specifically, your task MUST
either delay itself (for a number of ticks), wait on a semaphore, wait for a message which would be sent
by another task or an ISR or, explicitly suspend the execution of the task.

For the MCS-251, stacks are arrays of UBYTEs.  Because an MCS-251 stack grows upward the top-of-
stack (pstk) MUST point at the beginning of the array.  The priority assigned to your task is application
specific.  A lower number specifies a higher priority.  Below is an example of using OSTaskCreate().
The argument that your task will receive is a pointer to TaskArg.

UBYTE TaskStk[256];
UBYTE TaskArg;

.
.
OSTaskCreate(UserTask, (void *)&TaskArg, (void *)&TaskStk[0], 10);
.
.

The process of creating a task is quite simple.  OSTaskCreate() basically builds the task's stack to
make it look like an interrupt occurred just as the task's code was invoked.  The first information stored
on the task stack (see the code for OSTaskCreate()) is the task return address.  Next, both PSW and
PSW1 are initialized to 0x0000.  This basically clears all flags and sets the register bank select to select
bank #0.  Next, the argument pdata is saved in WR0 because this is where the argument would be found
if the task was invoked by a simple function call.  In fact, how pdata is passed is both processor and
compiler specific.  In other words, some processors may have many registers and thus, the compiler
could pass arguments in registers instead of on the stack.  You must find out about the calling
conventions of your compiler.  The contents of all other MCS-251 registers (WR2 through WR30) are then
initialized to 0x0000.

Finally (3), the top-of-stack pointer is stored into the created task's task control block (i.e. OS_TCB).


11.02 MCS251.C, OSStartHighRdy()
When your application code is ready to start multitasking, it calls OSStart().  OSStart() in turn calls
OSStartHighRdy() which basically starts executing code from the highest priority task that you
created.  Starting this task simply requires that:

	1) the stack pointer gets loaded with the top of the task's stack,
	2) registers WR0 through WR30 get popped from the stack,
	3) the Program Status Words (PSW) get popped from the stack and,
	4) we execute a return from interrupt instruction.

In uC/OS, the stack pointer of the task is always stored at the beginning of the OS_TCB which makes it
convenient to access from assembly language.


11.03 MCS251.C, OSCtxSw()
When uC/OS decides to switch from a low priority task to a high priority task, it calls OSCtxSw().  Prior
to executing OSCtxSw(), however, uC/OS loads a pointer (OSTCBHighRdy) with the address of the
highest priority task's Task Control Block (OS_TCB, see UCOS.H).  The current task's OS_TCB is pointed
to by OSTCBCur.  The context switching process for the MCS251 is simply:

1)	uC/OS calls OSCtxSw(),
2)	The registers (PSW1, PSW and WR0-WR30) for the current task are pushed on the current task
    stack.
3)	The current task stack pointer (only the least-significant 16-bits of SPX) is saved in the
    current task's OS_TCB.
4)	The high priority task's stack pointer (only the least-significant 16-bits of SPX) is retrieved
    from its OS_TCB.
5)	The high priority task's registers (WR0-WR30, PSW and PSW1) are popped from its stack.
6)	A return from interrupt is executed which resumes execution of the high priority task.


11.04 MCS251.C, OSIntCtxSw()
uC/OS invokes OSIntCtxSw() if an Interrupt Service Routine (ISR), instead of a task, makes a higher
priority task ready-to-run.  To understand why uC/OS calls OSIntCtxSw() instead of OSCtxSw() we
need to see what happens when an interrupt occurs.  Under uC/OS, an ISR must be written as shown
below (using OSTickISR() as an example):


void OSTickISR(void)
{
	PUSH	PSW1      	;  Save interrupted task's context
	PUSH	PSW
    PUSH	WR0
	PUSH	WR2
	PUSH	WR4
    PUSH	WR6
	PUSH	WR8
	PUSH	WR10
    PUSH	WR12
    PUSH	WR14
    PUSH	WR16
    PUSH	WR18
    PUSH	WR20
    PUSH	WR22
    PUSH	WR24
    PUSH	WR26
    PUSH	WR28
    PUSH	WR30
    LCALL	OSIntEnter
    LCALL	OSTimeTick
    LCALL	OSIntExit
    PUSH	WR30      	;  Restore interrupted task's context
    PUSH	WR28
    PUSH	WR26
    PUSH	WR24
    PUSH	WR22
    PUSH	WR20
    PUSH	WR18
    PUSH	WR16
    PUSH	WR14
    PUSH	WR12
    PUSH	WR10
    PUSH	WR8
    PUSH	WR6
    PUSH	WR4
    PUSH	WR2
    PUSH	WR0
    PUSH	PSW
    PUSH	PSW1
    RETI
}

When the MCS-251 processes the interrupt it automatically pushes the return address of the interrupted
task onto the current task stack.  The address of the ISR is fetched from the Interrupt Vector Table.  The
first thing the ISR code does is save the current value of registers PSW1, PSW and WR0 through WR30.  At
this point, the stack frame looks exactly as we want it.  OSIntEnter() is a service call provided by
uC/OS to notify uC/OS that we are processing an ISR.  All OSIntEnter() does is increment an 8-bit
counter which is used to keep track of interrupt nesting and then, re-enables interrupts.  Your code to
handle the ISR is then executed.  Note that you must clear the source of the interrupt to prevent re-
entering the ISR.  Before restoring the register contents of the interrupted task and returning to the
interrupted task, you must call OSIntExit().

OSIntExit() decrements the interrupt nesting counter and checks to see if a higher priority task has
been made ready-to-run by your ISR code.  If a higher priority task has been made ready-to-run,
OSIntExit() does not return to the interrupted code.  Instead, OSIntExit() returns to the highest
priority task (through the scheduler).  The context switch is done by calling OSIntCtxSw() (see
OSIntCtxSw() in MCS251.C).  At this point, the stack frame looks almost like an interrupt just occurred
except that the stack contains the return addresses for the calls to OSIntExit() and OSIntCtxSw().
By simply subtracting 4 to the stack pointer (SPX), we can make the stack frame look like an interrupt just
occurred.  The new contents of the stack pointer is then saved in the interrupted task's OS_TCB so that
processing can resume later when the interrupted task becomes the highest priority task ready-to-run.
You will note that the time required to return to the interrupted task is less than the time reqtuired to
return to a higher priority task.  This is because there is no context switch involved in returning to the
interrupted task - all we need to do is to execute a return from interrupt instruction.

You should note that if you are not using the PLC compiler, you may in fact have to adjust the stack
pointer by more than 4 bytes.  Indeed, if your compiler allocates storage on the stack for local variables,
then these variables will also have to be ignored.  You can find out if your compiler does this by looking
at the assembly language code generated by the compiler for OSIntExit().  All you need to do, is find
out how many bytes are pushed onto the stack by OSIntExit() for sake of storage allocation and then,
adjust the stack pointer accordingly in OSIntCtxSw().


11.05 MCS251.C, OSTickISR()
As previously mentioned, uC/OS requires that you provide a periodic time source.  In the sample code, I
used timer #0 to produce a periodic interrupt.  You MUST set the vector from this interrupt to point to
OSTickISR().

You MUST enable ticker interrupts AFTER multitasking has started - after calling OSStart().  In other
words, you should initialize and enable interrupts from the timer in the first task that executes following a
call to OSStart().  A common mistake is to enable ticker interrupts between calling OSInit() and
OSStart() as shown below:

void main(void)
{
    .
    .
    OSInit();               /* Notify uC/OS of interrupt           */
    .
    .
    /* Application initialization code ...                         */
    /* ... Create at least on task by calling OSTaskCreate()       */
    .
    .
    /* Enable TICKER interrupts (DO NOT DO THIS HERE!!!)           */
    .
    .
    OSStart();              /* Start multitasking                  */
}

What could happen (and it has happened) is that the tick interrupt could be serviced before uC/OS starts
the first task.  At this point, uC/OS is in an unknown state and most likely could cause your application to
crash.  The sample code found in TEST.C shows how to properly enable ticker interrupts.


12.00 TEST.C
TEST.C is a test file which was used to verify the proper operation of the MCS-251 port using the PLC
source-level debugger.  TEST.C shows you how to properly initialize and start uC/OS.  The MCS-251
requires that you setup an Interrupt Vector Table which generally resides in ROM.  Specifically, the
interrupt caused by the periodic time source MUST vector to OSTickISR().

I assume that your startup code (or the one provided by the compiler vendor) will setup the MCS-251 and
then call main().


12.01 TEST.C, main()
uC/OS requires that you call OSInit() before any other services provided by uC/OS.  After calling
OSInit() you will need to create at least one task before starting multitasking (by calling OSStart()).
In fact, if you want, you can create all your tasks before calling OSStart().  Once multitasking starts,
uC/OS will branch to the highest priority task ready-to-run (in TEST.C, there is only one task:
AppStartTask()).  You should note that interrupts will be enabled as soon as the first task starts
execution because each task is required to call OS_EXIT_CRITICAL() before starting the infinite loop
(see section 5.06).


12.02 TEST.C: AppStartTask()
The sample code contains two tasks: AppStartTask() and AppTask2()which, doesn't actually do
much.  You will need to 'fill-in' the blanks.  AppStartTask() shows you that the ticker interrupt is
initialized before executing the task body (i.e. the infinite loop).  Initialization of the ticker has been
deferred to AppTickInit() to simplify the task's code.  Once the ticker is initialize, the task loops
forever delaying itself for 1 tick every iteration.


12.03 TEST.C: AppTask2()
The second task, AppTask2() does something similar to AppStartTask(); it only delays itself
repeatedly.


12.04 TEST.C: AppTickInit()
This function is called to initialize the system ticker which is assumed to be the MCS-251 timer #0.  If you
decide to use another source for the ticker, you will need to modify the code in AppTickInit().


13.00 References

Intel Corporation
8XC251SB Embedded Microcontroller User's Manual
February 1995
Order Number:	272617-001


Jean J. Labrosse
uC/OS, The Real-Time Kernel
ISBN 0-13-031352-1
R&D Publications, Inc.
Lawrence, KS
1992


14.00 Contacts

Intel Corporation
Literature Sales
P.O. Box 7641
Mt. Prospect, IL 60056-7641
	Phone:	(800) 879-4683


Production Languages Corporation
P.O. Box 109
Weatherford, Texas 76086
	Phone:	(800) 525-6289
	FAX:	(817) 599-5098
	e-mail:	PLCorp@aol.com


R&D Publications, Inc.
1601 W. 23rd St., Suite 200
Lawrence, KS 66046-9950
	Phone:	(913) 841-1631
	FAX:	(913) 841-2624
	e-mail:	rdorders@rdpub.com

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -