??? 09/24/08 16:51 Read: times Msg Score: +1 +1 Informative |
#158575 - An alternative Responding to: ???'s previous message |
Maarten Brock said:
What usually goes hand-in-hand with a super-loop is the notion of state-machines. Every 'thread' in your mindset can be implemented as a state-machine. And all state-machines are called sequentially in the super-loop. Obviously the state-machines may not lock up. Together they perform a form of cooperative multitasking. Or, you could just go ahead and use a real cooperative multitasker. To save somone else the trouble of pointing out the obvious, I am fully aware that the 8051 architecture isn't the best for this sort of thing. I am also fully aware of the difficulties surrounding the use of function pointers with some 8051 C compilers. That said, however, it can be done, as shown below. Whether or not it's a good idea in any particular situation is a matter of engineering. Please view what follows as a detailed sketch, and not as ready-to-run code, for two reasons: 1. A bit of it depends on symbols defined in header files that I have not supplied. 2. A bit of it is compiler dependent. -- Russ ------------------------------------------------------------------- What Is 8051Task? ----------------- 8051Task is a library of C functions that lets you run one or more C functions together in a priority-based, cooperative (non-preemptive) multitasking environment. 8051Task provides full support for intertask synchronization, intertask communication, and timed delays. 8051Task provides a more capable scheduler and better intertask communication than do simple round-robin task switchers such as Wayne Conrad's MTASK or the system described in the October, 1988 issue of "Computer Language" magazine, while avoiding the complexity of a full-blown interrupt-driven, preepmtive system like Thomas Wagner's CTask. What is Multitasking? --------------------- Multitasking is a way to divide a single program into several distinct threads of execution called "tasks" which execute concurrently under the control of a supervisory program called a "scheduler". The tasks are usually somewhat independent of one another (although they can communicate among themselves) and appear to execute all at once. On a single-CPU system, of course, only one task can be running at any given instant, but the scheduler passes control to the various tasks in a way that gives the illusion that they are all running at the same time. Multitasking as implemented by 8051Task is not to be confused with multiprogramming (as provided by Windows or an operating system like QNX), in which multiple -programs- appear to run at the same time, or with multiprocessing, which requires a computer with more than one CPU. When you use 8051Task, you compile and link all your tasks together into a single program which works just like any other program. Preemptive vs. Cooperative Multitasking --------------------------------------- In a "preemptive" multitasking system, the scheduler doles out CPU time to the tasks in chunks called "time slices". Each task is allowed to execute for a specific time period, after which control is wrested from it by the timer interrupt and given to some other task by the scheduler. The advantage of such a scheme is that tasks can be written with little or no awareness of the multitasking system. The downside, however, is that it can be difficult to make sure that tasks are not interrupted at inappropriate times. As an example, consider a task which has just made a call to the C runtime library. If it is interrupted while it is within the library routine, and control is then passed to a different task which also calls the library, havoc will reign shortly if the library is not reentrant. In a "cooperative" multitasking environment, a task that is running continues to run until it explicitly relinquishes control by making a call to the multitasking system. As a result, such tasks must be written so that they do in fact give up the CPU appropriately. Concern about being preempted at a bad time is eliminated, however, because the tasks surrender control voluntarily at times of their own choosing rather than having the rug jerked from under them by an interrupt. 8051Task implements cooperative multitasking. How Does 8051Task Work? ----------------------- 8051Task tasks are written as independent C functions which are linked together with functions from the 8051Task library (and possibly other libraries) to form the complete application. The tasks are rarely (if ever) called explicitly by user-written code. Instead, each task is identified to the 8051Task scheduler, usually when the application is first started, by a call to create_task(). From then on, the scheduler takes care of placing the tasks into execution, based on events within the application and upon the tasks' relative priorities. A key concept in understanding how the scheduler works is that of a task's "state". At any instant, each task is in exactly one of the following states: RUNNING The RUNNING task is the one currently executing. There can obviously be only one RUNNING task, since there is only one CPU. READY READY tasks are those which are not currently blocked from execution waiting for some external event. At each scheduling period, the READY task with the highest priority is made the RUNNING task and is placed into execution. If several READY tasks all have the same priority, they will run in a round-robin fashion. SLEEPING SLEEPING tasks are those blocked from execution until some specified future time. SLEEPING tasks are made READY when the specified time arrives. WAITING WAITING tasks are those waiting for some event to occur, such as a message from another task, availability of a character from the keyboard, or some interrupt or other. WAITING tasks may also have a timeout associated with them, in which case they will wait only for a specified time before being made READY again. When a task is first created, it is placed in the READY state, and it will begin to execute as soon as all other higher-priority tasks become blocked (either SLEEPING or WAITING). It will continue to run until it actively relinquishes control, either explicitly by a call to yield() or suspend(), or implicitly by a call to one of the other 8051Task functions. 8051Task Function Overviews --------------------------- This section gives a brief functional description of each 8051Task call, and is organized with related functions grouped together. Complete details are to be found in the source code. Overhead Functions ------------------ start_8051Task() Initializes the 8051Task software. This function must be called before any other 8051Task functions are used. Task Manipulation ----------------- create_task() Establishes a specified C function as an 8051Task task and makes it READY. kill_task() Kills a specified task. yield() Makes the calling task READY and then invokes the scheduler. Use of this call is the key to the "cooperative" nature of 8051Task scheduling. If a task plans to run for a long time without either waiting for an event or going to sleep, it should call yield periodically to give tasks with higher priority a chance to use the CPU. suspend() Suspends execution of the calling task for a specified time. Mailbox Functions ----------------- Tasks can send messages of unspecified format to each other using mailboxes. Mailboxes provide a FIFO queue of messages which is initially empty. Messages sent to a mailbox are simply added to the queue, while tasks reading from the mailbox wait until a message is present and get the oldest such message. create_mailbox() Creates a mailbox. send_mail() Sends a message to a mailbox. get_mail() Waits until a message is present at a mailbox, and returns it to the caller. A timeout may be specified. check_mail() Checks if a mailbox contains anything or not. Semaphore Functions ------------------- Tasks can synchronize with each other for resource sharing and so on using semaphores. Once created, each semaphore maintains a count. Tasks signalling the semaphore increase the count by one, and whenever a task waiting at the semaphore is made READY, the count is decreased by one. The count can be initialized to any desired value when the semaphore is created. create_sem() Creates a semaphore. signal_sem() Signals a semaphore. wait_sem() Waits for a semaphore. A timeout may be specified. check_sem() Returns the value of a semaphore's count. Miscellaneous Functions ----------------------- get_status() Returns the termination status (either TIMEOUT or OKAY) of the most recent call to suspend(), get_mail(), wait_key(), or wait_sem(). /* //////////////////////////////////////////////////////////////////////////// 8051task.h /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: This is the external interface to the 8051 multitasking system. REVISIONS: 12 Jun 05 - RAC - Genesis //////////////////////////////////////////////////////////////////////////// */ #ifndef TASK8051_H #define TASK8051_H /* //////////////////////////////////////////////////////////////////////////// Names for Numbers //////////////////////////////////////////////////////////////////////////// */ #define STANDARD_PRIORITY 100 /* Priority of Joe Average task */ enum { OKAY, TIMEOUT }; /* Wait return status values */ /* //////////////////////////////////////////////////////////////////////////// Data Structure Definitions //////////////////////////////////////////////////////////////////////////// */ #define TDESC struct task_descriptor TDESC { /* The Task Descriptor */ char taskName[20]; /* Text task name for debugging */ TDESC *prevWait; /* Links to other tasks waiting at a */ TDESC *nextWait; /* mailbox or semaphore */ TDESC *prevTime; /* Links to other tasks waiting on */ TDESC *nextTime; /* the timer */ long timeout; /* Timer expiration time */ unsigned priority; /* 0 = lowest, 0xFFFF = highest */ char taskNumber; /* My task number */ unsigned mySP; /* Saved SP register */ unsigned waitStatus; /* Status of last wait operation, or */ /* semaphore count */ void *msg; /* Pointer to place to put mail, or */ /* to oldest queued mailbox message */ union { unsigned *mailSize; /* Pointer to place to put mail size */ unsigned semCount; } tt; }; /* //////////////////////////////////////////////////////////////////////////// Function Prototypes //////////////////////////////////////////////////////////////////////////// */ void Start8051Task(void); void Suspend(long); char CreateTask(char *, void (*)(), unsigned); void Yield(void); void KillTask(char); int SendMail(void *, unsigned, TDESC*); void GetMail(void *, unsigned *, TDESC*, long, int); int CheckMail(TDESC *); int GetStatus(void); void SignalSem(TDESC *); void WaitSem(TDESC *, long); unsigned CheckSem(TDESC *); long GetTicks(void); long GetTicksI(void); #endif /* End #ifndef TASK8051_H */ /* //////////////////////////////////////////////////////////////////////////// 8051task.c /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: This is the implementation of the 8051 multitasking system. REVISIONS: 12 Jun 05 - RAC - Genesis //////////////////////////////////////////////////////////////////////////// */ #include <stdlib.h> #include <string.h> #include "8051task.h" /* //////////////////////////////////////////////////////////////////////////// Names for Numbers //////////////////////////////////////////////////////////////////////////// */ /* This is the maximum number of tasks that can be in the system at one time. Note that main() counts as a task unless and until it returns. Change this number and recompile to suit the application. (Each task uses about 300 bytes of external RAM, so it's worthwhile to minimize NUM_TASKS if RAM is in short supply.) */ #define NUM_TASKS 10 /* //////////////////////////////////////////////////////////////////////////// Data Structure Definitions //////////////////////////////////////////////////////////////////////////// */ #define MQE struct msg_queue_entry MQE { MQE *nextMsg; /* Link to next message in mailbox */ unsigned msgSize; /* Size of this message */ char *msgText; /* Pointer to message text */ }; /* //////////////////////////////////////////////////////////////////////////// External Function Prototypes //////////////////////////////////////////////////////////////////////////// */ void SaveStack(char *); /* These are defined in stackers.s03 */ void RestoreStack(char *); unsigned Fixit(void (*)()); /* //////////////////////////////////////////////////////////////////////////// Local Function Prototypes //////////////////////////////////////////////////////////////////////////// */ static void TimerLink(TDESC *); static void Schedule(void); static void WaitUnlink(TDESC *); static void TimeUnlink(TDESC *); static void ReadyLink(TDESC *); static void AutoKill(void); /* //////////////////////////////////////////////////////////////////////////// Module Variables //////////////////////////////////////////////////////////////////////////// */ TDESC rlh; /* Dummy READY list header */ TDESC tlh; /* Dummy timer list head */ TDESC tasks[NUM_TASKS]; /* The task descriptors */ char stacks[NUM_TASKS][256]; /* Task stack images */ TDESC *currentTask; /* Ptr to the RUNNING task */ long gnow; /* Ticks since timers started */ unsigned tickCounter; /* Count clock ticks here */ /* //////////////////////////////////////////////////////////////////////////// Start8051Task() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Initializes the multitasker. This function must be called before any other 8051task functions are used. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask routine //////////////////////////////////////////////////////////////////////////// */ void Start8051Task() { char j; /* A generic integer */ rlh.priority = 0xFFFF; /* Keep READY header first */ for (j=0; j<NUM_TASKS; j++) { /* Mark all the tasks as */ tasks[j].taskNumber = 255; /* available */ } strcpy(tasks[0].taskName, "main"); /* Set up the main task */ tasks[0].priority = STANDARD_PRIORITY; /* Standard user priority */ tasks[0].taskNumber = 0; /* Its descriptor is in use */ currentTask = tasks + 0; /* Make it the current task */ } /* End Start8051Task() */ /* //////////////////////////////////////////////////////////////////////////// Suspend() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Suspends a tasks for a given period of time. The time is given in clock ticks, which occur at a rate determined by the timer interrupt handler. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask function //////////////////////////////////////////////////////////////////////////// */ void Suspend( long ticks /* Sleep for <ticks> clock ticks */ ) { currentTask->timeout = ticks + gnow; /* Timer expiration time */ TimerLink(currentTask); /* Stick in timer list */ Schedule(); /* Go do something else */ } /* End suspend() */ /* //////////////////////////////////////////////////////////////////////////// TimerLink() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Internal routine to add entries to the timer list. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask function //////////////////////////////////////////////////////////////////////////// */ static void TimerLink(TDESC *neu) { TDESC *before; /* Temp TDESC pointers */ TDESC *after; before = &tlh; /* Start at timer header */ after = before->nextTime; while (after != NULL && /* Search for where to put */ after->timeout < neu->timeout) { /* the new entry */ before = after; after = after->nextTime; } neu->prevTime = before; /* Add the new guy to the */ neu->nextTime = after; /* list */ before->nextTime = neu; if (after != NULL) after->prevTime = neu; } /* End TimerLink() */ /* //////////////////////////////////////////////////////////////////////////// TimeUnlink() WaitUnlink() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Internal routines to remove entries from the timer queue or one of the wait queues. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask functions //////////////////////////////////////////////////////////////////////////// */ #define A B->prevTime #define C B->nextTime static void TimeUnlink(TDESC *B) { /* The entry to remove */ if (A != NULL) { /* If it is in fact queued, */ A->nextTime = C; /* wire pointers around */ if (C != NULL) C->prevTime = A; /* it, and then */ A = C = NULL; /* unlink it completely */ } /* End 'it is queued' */ } /* End TimeUnlink() */ #undef A #undef C #define A B->prevWait #define C B->nextWait /* ///////////////////////////////////////////////////////////////////////// */ static void WaitUnlink(TDESC *B) { /* The entry to remove */ if (A != NULL) { /* If it is in fact queued, */ A->nextWait = C; /* wire pointers around */ if (C != NULL) C->prevWait = A; /* it, and then */ A = C = NULL; /* unlink it completely */ } /* End 'it is queued' */ } /* End WaitUnlink() */ #undef A #undef C /* //////////////////////////////////////////////////////////////////////////// ReadyLink() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Internal routine to add entries to the READY list. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask function //////////////////////////////////////////////////////////////////////////// */ static void ReadyLink(TDESC *neu) { TDESC *before; /* Temp TDESC pointers */ TDESC *after; before = &rlh; /* Start at READY header */ after = before->nextWait; while (after != NULL && /* Search for where to put */ after->priority >= neu->priority) { /* the new entry */ before = after; after = after->nextWait; } neu->prevWait = before; /* Link new guy into the */ neu->nextWait = after; /* READY list */ before->nextWait = neu; if (after != NULL) after->prevWait = neu; } /* End ready_link() */ /* //////////////////////////////////////////////////////////////////////////// Schedule() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Where the rubber meets the road. This routine does some housekeeping related to the timer, saves the context of the RUNNING task, then activates the READY task with the highest priority. REVISIONS: 12 Jun 05 - RAC - Adapted from the similar RMAXTask function //////////////////////////////////////////////////////////////////////////// */ static void Schedule() { unsigned tc; /* Copy of tick counter */ TDESC *top; /* Ptr to 1st timer q entry */ do { /* Do 'til task is READY */ /* If a recent timer tick has occurred, make READY all the tasks in the timer queue whose time has expired as a result of the recent tick. */ if (tickCounter) { /* If a tick has happened */ ET0 = 0; /* Timer 0 interrupt off */ tc = tickCounter; /* Read and reset the "real" */ tickCounter = 0; /* tick counter */ ET0 = 1; /* Timer interrupt back on */ gnow += tc; /* Update local time */ for (;;) { /* We'll break from this */ top = tlh.nextTime; /* 1st timer queue entry */ if ((top == NULL) || /* If no entry, or it has */ (top->timeout > gnow)) { /* not yet timed out, we're */ break; /* done */ } TimeUnlink(top); /* Yank task from timer list */ WaitUnlink(top); /* and wait list, too */ top->waitStatus = TIMEOUT; /* It timed out */ ReadyLink(top); /* Add to READY list */ } /* End 'for expired timers' */ } /* End 'a tick has happened' */ } while (rlh.nextWait == NULL); /* End 'do til task READY' */ /* Save the current task's stack and stack pointer */ SaveStack(&(stacks[currentTask->taskNumber][0])); currentTask->mySP = SP; /* Switch to the new task */ currentTask = rlh.nextWait; /* The new current task */ rlh.nextWait = currentTask->nextWait; /* Unlink it from READY list */ if (currentTask->nextWait != NULL) { currentTask->nextWait->prevWait = &rlh; } currentTask->prevWait = NULL; /* Unlink it completely */ currentTask->nextWait = NULL; /* Restore the new task's stack and stack pointer */ SP = currentTask->mySP; RestoreStack(&(stacks[currentTask->taskNumber][0])); } /* End Schedule() */ /* //////////////////////////////////////////////////////////////////////////// AutoKill() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Internal function that kills tasks that return from their main level functions. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void AutoKill() { currentTask->taskNumber = 255; /* Mark descr as available */ Schedule(); /* Press on */ } /* End AutoKill() */ /* //////////////////////////////////////////////////////////////////////////// KillTask() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Allows one task to murder another. NOTES: This function must be used with extreme care to make sure that the task being killed has his affairs in order. He should not "own" any dynamic memory, he should not have any open files, etc. A task should NOT use this function to kill itself. Tasks wishing to commit suicide should do so by simply returning from themselves, either by an explicit return or by simply running off the end of themselves. Tasks contemplating suicide should also make sure their affairs are in order as noted above. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void KillTask(char victim) { TimeUnlink(tasks + victim); WaitUnlink(tasks + victim); tasks[victim].taskNumber = 255; /* Mark descr as available */ } /* End KillTask() */ /* //////////////////////////////////////////////////////////////////////////// CreateTask() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Establishes a specified C function as an 8051Task task and makes it READY. REVISIONS: 16 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ char CreateTask ( /* New task number, or 255 if error */ char *name, /* Pointer to text name for task */ void (*fn)(), /* Ptr to the code for the task */ unsigned priority /* Desired task priority */ ) { char j; /* A generic integer */ unsigned u; /* A generic unsigned */ /* Find an available task descriptor, or die trying */ j = 0; /* Start with the first task */ while (1) { /* We'll break from this */ if (tasks[j].taskNumber==255) { /* Abandon loop as soon as */ break; /* we find an available */ } /* descriptor */ if (++j == NUM_TASKS) { /* Abandon ship if there are */ return 255; /* no available descriptors */ } } /* End 'while (1)' */ /* Now just set up the descriptor and stick it in the READY list. */ strcpy(tasks[j].taskName, name); tasks[j].priority = priority; /* Caller-specified priority */ tasks[j].taskNumber = j; /* Task # = tasks[] index */ tasks[j].mySP = 11; /* Room for two return addrs */ u = Fixit(AutoKill); /* Get pointer to AutoKill */ stacks[j][8] = u & 0xFF; /* Set up to kill task if it */ stacks[j][9] = (u >> 8) & 0xFF; /* ever returns */ u = Fixit(fn); stacks[j][10] = u & 0xFF; /* Set up so we can just */ stacks[j][11] = (u >> 8) & 0xFF; /* return to the new task */ ReadyLink(tasks+j); /* Put in the ready list */ return j; /* Return the task number */ } /* End CreateTask() */ /* //////////////////////////////////////////////////////////////////////////// Yield() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Makes the calling task READY and then invokes the scheduler. If a task plans to run for a long time without either waiting for an event or going to sleep, it should call this routine periodically to allow tasks with equal or higher priority the chance to run. REVISIONS: 16 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void Yield() { ReadyLink(currentTask); Schedule(); } /* End yield() */ /* //////////////////////////////////////////////////////////////////////////// SendMail() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Sends a message to a mailbox. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ int SendMail ( /* 1 if all is well, 0 if error */ void *msg, /* Pointer to message to send */ unsigned size, /* Size of message to send */ TDESC *mailbox /* Pointer to destination mailbox */ ) { TDESC *wt; /* Ptr to waiting task */ MQE *mqep; /* Temp message queue ptr */ if ((wt = mailbox->nextWait) != NULL) { /* There is a task waiting! */ memcpy(wt->msg, /* Copy message to that */ msg, /* task's buffer */ size); *(wt->tt.mailSize) = size; /* Return size as well */ TimeUnlink(wt); /* Yank task from timer list */ WaitUnlink(wt); /* and wait list, too */ wt->waitStatus = OKAY; /* The read was successful */ ReadyLink(wt); /* Add to READY list */ } /* End 'task is waiting */ else { /* No task waiting */ if (mailbox->msg == NULL) { /* This is 1st message */ mailbox->msg = calloc(1, sizeof(MQE)); /* Make space for new */ if (mailbox->msg == NULL) /* message, or die */ return 0; /* trying */ mqep = (MQE *)mailbox->msg; /* Go to new message */ } /* End '1st message' */ else { /* Not 1st message */ mqep = (MQE *)mailbox->msg; /* Point at 1st message */ while (mqep->nextMsg != NULL) /* Find end of list of */ mqep = mqep->nextMsg; /* messages in this mailbox */ mqep->nextMsg = (MQE *)calloc(1, sizeof(MQE)); /* Make space */ if (mqep->nextMsg == NULL) /* for new message, or die */ return 0; /* trying */ mqep = mqep->nextMsg; /* Success ... go to new msg */ } /* End 'not 1st message' */ mqep->nextMsg = NULL; /* Terminate chain */ mqep->msgSize = size; /* Set up message size */ mqep->msgText = (char *)calloc(1, size); /* Allocate space for */ if (mqep->msgText == NULL) /* message text, or die */ return 0; /* trying */ memcpy(mqep->msgText, /* Copy actual message to */ msg, /* the list */ size); } /* End 'no task waiting' */ return 1; /* Success */ } /* End send_mail() */ /* //////////////////////////////////////////////////////////////////////////// GetMail() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Waits at a mailbox for mail or a timeout. * REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void GetMail( void *recipMsg, /* Where to put result */ unsigned *mailSize, /* Where to put size of result */ TDESC *mailbox, /* Pointer to mailbox to use */ long timeout, /* Give up this many ticks from now */ int reverse /* Reverse normal queueing order */ ) { MQE *mqep; /* Temp message queue ptr */ mqep = (MQE *) mailbox->msg; if (mqep != NULL) { /* There is mail */ memcpy(recipMsg, /* Copy it to recipient */ mqep->msgText, mqep->msgSize); *mailSize = mqep->msgSize; /* Return size as well */ mailbox->msg = mqep->nextMsg; /* Unlink the mail just read */ free(mqep->msgText); /* Free message text */ free(mqep); /* Free message queue entry */ currentTask->waitStatus = OKAY; /* The read was successful */ } /* End 'there is mail' */ else { /* There is no mail */ if (!reverse) while (mailbox->nextWait != NULL) /* Find end of list of tasks */ mailbox = mailbox->nextWait; /* waiting on this mailbox, */ currentTask->nextWait = mailbox->nextWait; /* Terminate the list */ mailbox->nextWait = currentTask; /* and add the current task */ currentTask->prevWait = mailbox; /* Complete linkage */ if (currentTask->nextWait != NULL) /* RAC 041292 */ (currentTask->nextWait)->prevWait /* RAC 041292 */ = currentTask; /* RAC 041292 */ currentTask->msg = recipMsg; /* Note where the result */ currentTask->tt.mailSize = mailSize; /* should be put eventually */ if (timeout) { /* If timeout was given, */ currentTask->timeout = /* figure out when timer */ timeout + gnow; /* should expire */ TimerLink(currentTask); /* Stick in timer list */ } Schedule(); /* Go do something else */ } /* End 'no mail' */ } /* End get_mail() */ /* //////////////////////////////////////////////////////////////////////////// CheckMail() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Tells if a particular mailbox has mail or not. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ int CheckMail( /* 1 if there is mail, else 0 */ TDESC *mailbox /* Ptr to mailbox of interest */ ) { return (mailbox->msg == NULL) ? 0 : 1; } /* //////////////////////////////////////////////////////////////////////////// GetStatus() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: For the current task, returns the status of the most recent call to Suspend(), GetMail(), or WaitSem(). NOTE: This routine will always return TIMEOUT following a call to Suspend(). REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function //////////////////////////////////////////////////////////////////////////// */ int GetStatus(void) { /* TIMEOUT or OKAY */ return currentTask->waitStatus; } /* //////////////////////////////////////////////////////////////////////////// SignalSem() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Signals a semaphore. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void SignalSem ( TDESC *semaphore /* Ptr to semaphore of interest */ ) { TDESC *wt; /* Ptr to waiting task */ if ((wt = semaphore->nextWait) != NULL) { /* There is a task waiting! */ wt->waitStatus = OKAY; /* The read was successful */ TimeUnlink(wt); /* Yank task from timer list */ WaitUnlink(wt); /* and wait list, too */ ReadyLink(wt); /* Add to READY list */ } /* End 'task is waiting' */ else { /* No task waiting */ semaphore->tt.semCount += 1; /* Bump count */ } /* End 'no task waiting' */ } /* End SignalSem() */ /* //////////////////////////////////////////////////////////////////////////// WaitSem() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Waits for a semaphore. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ void WaitSem ( TDESC *semaphore, /* Ptr to semaphore of interest */ long timeout /* Give up this many ticks from now */ ) { if (semaphore->tt.semCount) { /* Semaphore is set */ semaphore->tt.semCount -= 1; /* Decrement count */ currentTask->waitStatus = OKAY; /* The read was successful */ } /* End 'semaphore is set' */ else { /* Semaphore clear */ while (semaphore->nextWait != NULL) /* Find end of list of tasks */ semaphore = semaphore->nextWait; /* waiting on this semaphore, */ semaphore->nextWait = currentTask; /* and add the current task */ currentTask->nextWait = NULL; /* Terminate the list */ currentTask->prevWait = semaphore; /* Complete linkage */ if (timeout) { /* If timeout was given, */ currentTask->timeout = /* figure out when timer */ timeout + gnow; /* should expire */ TimerLink(currentTask); /* Stick in timer list */ } Schedule(); /* Go do something else */ } /* End 'semaphore clear' */ } /* End WaitSem() */ /* //////////////////////////////////////////////////////////////////////////// CheckSem() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: Returns a semaphore count. REVISIONS: 26 Jun 05 - RAC - Adapted from the similar RMAXTask function. //////////////////////////////////////////////////////////////////////////// */ unsigned CheckSem ( /* The semaphore count */ TDESC *semaphore /* Ptr to semaphore of interest */ ) { return semaphore->tt.semCount; /* Return count */ } /* //////////////////////////////////////////////////////////////////////////// GetTicks() GetTicksI() /////////////////////////////////////////////////////////////////////////////// DESCRIPTION: GetTicks() returns the value of a counter that is set to zero at boot time and incremented at TICKS_PER_SECOND Hz forever thereafter. If TICKS_PER_SECOND is 40 (as it probably is; check mtc.h to make sure), the counter will run for about 621 days before overflowing. GetTicksI() is exactly the same, to be used within ISRs to avoid re-entrancy problems. REVISIONS: 5 Mar 07 - RAC - Genesis 19 Apr 07 - RAC - Added GetTicksI() //////////////////////////////////////////////////////////////////////////// */ long GetTicks() { return gnow; } long GetTicksI() { return gnow; } ;------------------------------------------------------------------------------ ; stackers.s03 ;------------------------------------------------------------------------------ ; DESCRIPTION: This module contains a couple of assembly language routines to ; move copies of the stack back and forth from internal RAM to ; buffers in external RAM. ; ; REVISIONS 15 Jun 05 - RAC - Genesis ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ; External Variable Definitions ;------------------------------------------------------------------------------ EXTERN _R ; REGISTER BANK (LINKER SETS THIS UP) RSEG RCODE ;------------------------------------------------------------------------------ ; SaveStack() ; RestoreStack() ;------------------------------------------------------------------------------ ; DESCRIPTION: SaveStack() moves the stack image from internal RAM to a buffer ; in external RAM whose address is passed by the caller. The ; address is passed in R6 (low byte) and R7 (high byte). ; RestoreStack() is identical except it moves the stack image ; from the caller's buffer to internal RAM. ; ; REVISIONS 15 Jun 05 - RAC - Genesis ;------------------------------------------------------------------------------ PUBLIC SaveStack SaveStack: MOV DPH,R6 ; Move external RAM pointer from R6, R7 MOV DPL,R7 ; to DPTR MOV R0,#0 ; Use R0 to index the internal RAM Sloop: MOV A,@R0 ; Get byte from internal RAM MOVX @DPTR,A ; Stuff into external RAM buffer INC DPTR ; Bump external RAM pointer INC R0 ; Bump interanl RAM index MOV A,R0 ; RAM index to accumulator for compare CJNE A,SP,Sloop ; Spin until top of the stack RET ; Done ;------------------------------------------------------------------------------ PUBLIC RestoreStack RestoreStack: MOV DPH,R6 ; Move external RAM pointer from R6, R7 MOV DPL,R7 ; to DPTR MOV R0,#0 ; Use R0 to index the internal RAM RLoop: MOVX A,@DPTR ; Get byte from external RAM buffer MOV @R0,A ; Stuff into internal RAM INC DPTR ; Bump external RAM pointer INC R0 ; Bump interanl RAM index MOV A,R0 ; RAM index to accumulator for compare INC A CJNE A,SP,RLoop ; Spin until top of the stack RET ;------------------------------------------------------------------------------ ; Fixit() ;------------------------------------------------------------------------------ ; DESCRIPTION: Grabs the address of a task being created from a pointer to it ; that is also in the code segment, and therefore difficult to ; access from C. Or so it seems. ; ; REVISIONS: 16 Jun 05 - RAC - Genesis ;------------------------------------------------------------------------------ PUBLIC Fixit Fixit: MOV DPH,R6 ; Get pointer to task address MOV DPL,R7 CLR A MOVC A,@A+DPTR ; Get high byte of address MOV R6,A ; Save for return MOV A,#1 MOVC A,@A+DPTR ; Get low byte of address MOV R7,A ; Save for return RET END |
Topic | Author | Date |
Multi Threading in C for 89S8253 | 01/01/70 00:00 | |
effective? | 01/01/70 00:00 | |
Protothreads | 01/01/70 00:00 | |
What do you mean? | 01/01/70 00:00 | |
What actually you are looking for | 01/01/70 00:00 | |
Seek Help ! for Serial Communication | 01/01/70 00:00 | |
start a new thread | 01/01/70 00:00 | |
Good reply... | 01/01/70 00:00 | |
hehe... | 01/01/70 00:00 | |
purpose | 01/01/70 00:00 | |
What tasks? | 01/01/70 00:00 | |
tasks | 01/01/70 00:00 | |
Superloop | 01/01/70 00:00 | |
ok...one more thing | 01/01/70 00:00 | |
If Key == '1' Then | 01/01/70 00:00 | |
statemachine | 01/01/70 00:00 | |
An alternative | 01/01/70 00:00 | |
why not in FAQ or personal page? | 01/01/70 00:00 | |
Yes | 01/01/70 00:00 | |
An alternative ? | 01/01/70 00:00 | |
Your Mileage May Vary | 01/01/70 00:00 | |
thanks alot![]() | 01/01/70 00:00 |