/*
********************************************************************************************************
* CRTOS_V2.c RTOS KERNEL SOURCE CODE
*
* Co-Operative RTOS written in C based on PaulOS by Ing. Paul P. Debono :
* ———————————————————————–
*
* For use with the 8051 family of microcontrollers
*
* Notes:
*
* Timer to use for the RTOS ticks is user selectable, Timer 0, 1 or 2
* Naturally, Timer 2 can only be used with an 8032 CPU.
*
* Assign the correct values to ‘TICK_TIMER’, ‘CPU’, ‘MAINSTACK’ and ‘NOOFTASKS’ in CRTOS_V2.h
*
* If it is noticed that timing parameters are not being met well – the system’s TICKTIME
* can be modified by changing the value ‘TICKTIME’ in CRTOS_V2.h – please adhere to the
* conditions mentioned in CRTOS_V2.h
*
* File : CRTOS_V2.c
* Revision : 7C
* Date : FEBRUARY 2005
* By : John Blaut, Paul P. Debono
*
* B. Eng. (Hons.) Elec. Course Final Year Project
* University Of Malta
*
********************************************************************************************************
*/
/*
********************************************************************************************************
* INCLUDES
********************************************************************************************************
*/
#include <reg52.h> /* 8052 Special Function Registers 8052 */
#include “CRTOS_V2.h” /* CRTOS system calls definitions (IN PROJECT DIRECTORY) */
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* GLOBAL VARIABLES
********************************************************************************************************
*/
uchar data * data ReadyQTop; // Address of last ready task
uchar data Running; // Number of current task
bit bdata IntFlag; // Flag indicating a task waiting for an interrupt was found
bit bdata TinQFlag; // Flag indicating that a task timed out
bit bdata Priority; // Flag indicating whether priority is enabled or disabled
uchar data ReadyQ[NOOFTASKS + 2]; // Queue stack for tasks ready to run
#if (CPU == 8032)
uchar idata SPTs[NOOFTASKS + 1]; // SP for each task and one for the idle (main) task
uchar idata TaskFlags[NOOFTASKS + 1]; // Bytes storing flags for each task and idle (main) task
#elif (CPU == 8051)
uchar xdata SPTs[NOOFTASKS + 1]; // SP for each task and one for the idle (main) task
uchar xdata TaskFlags[NOOFTASKS + 1]; // Bytes storing flags for each task and idle (main) task
// stored in external RAM since internal RAM is only 128 bytes
#endif
ulong xdata TTs[NOOFTASKS]; // Array storing remaining timeout time for each task
ulong xdata IntvalCnt[NOOFTASKS]; // Array storing remaining periodic interval time for each task
ulong xdata IntvalRld[NOOFTASKS]; // Array storing the periodic interval time value for each task
uchar xdata Ext_Stack[(NOOFTASKS + 1) * STACKSIZE]; // Area in external RAM reserved for saving the
// stack of each task including the idle (main) task
#if (STACK_CHECK)
uchar xdata MaxSPTs[NOOFTASKS + 1] = { MAINSTACK – 1 }; // Bytes storing maximum value attained by SP of each task in order
#endif // to calculate each task’s maximum stack size in case STACK_CHECK
// is defined in CRTOS.h
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* FUNCTION DEFINITIONS
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : INIT_RTOS
*
* Function type : Initialisation System call
*
* Description : This system call initialises the RTOS variables, task SPs and enables any required
* interrupts
*
* Arguments : iemask Represents the interrupt enable mask which is used to set up the IE special
* function register. Its value determines which interrupts will be enabled
* during the execution of the user’s application.
*
* Returns : None
*
********************************************************************************************************
*/
void INIT_RTOS(uchar iemask)
{
uint idata i;
#if (TICK_TIMER == 0)
IE = (iemask & 0x7f) | 0x02; /* Set up 8051 IE register, using timer 0 */
IP = 0x02; /* Assign scheduler interrupt high priority */
#elif (TICK_TIMER == 1)
IE = (iemask & 0x7f) | 0x08; /* Set up 8051 IE register, using timer 1 */
IP = 0x08; /* Assign scheduler interrupt high priority */
#elif (TICK_TIMER == 2)
IE = (iemask & 0x7f) | 0x20; /* Set up 8051 IE register, using timer 2 */
IP = 0x20; /* Assign scheduler interrupt high priority */
#endif
for (i = 0; i < ((NOOFTASKS + 1) * STACKSIZE); i++)
Ext_Stack[i] = 0;
Running = IDLE_TASK; /* Set idle task as the running task */
for (i = 0; i < NOOFTASKS; i++)
{
TTs[i] = NOT_TIMING; /* Initialise task timeouts, */
IntvalCnt[i] = NOT_TIMING; /* periodic interval count */
IntvalRld[i] = NOT_TIMING; /* and reload variables. */
ReadyQ[i] = IDLE_TASK; /* Fill the READY queue with */
} /* with the idle task */
ReadyQ[NOOFTASKS] = IDLE_TASK;
ReadyQ[NOOFTASKS + 1] = IDLE_TASK;
ReadyQTop = ReadyQ; /* Pointer to last task made to point to */
/* base of the queue. */
for (i = 0; i < NOOFTASKS + 1; i++)
{
SPTs[i] = MAINSTACK + 2; /* Initialise task SP values */
TaskFlags[i] = 0; /* Initialise task status bytes */
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : CREATE
*
* Function type : Initialisation System call
*
* Description : This system call is used in the main program for each task to be created for use in
* the application.
*
* Arguments : task Represents the task number (1st task is numbered as 0).
*
* taskadd Represents the task’s start address, which in the C environment, would
* simply be the name of the procedure
*
* Returns : None
*
********************************************************************************************************
*/
void CREATE(uchar task, uint taskadd)
{
ReadyQTop++; /* Task is added to next available */
*ReadyQTop = task; /* position in the READY queue. */
Ext_Stack[STACKSIZE * task] = taskadd % 256;
Ext_Stack[(STACKSIZE * task) + 1] = taskadd / 256;
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : RTOSGO
*
* Function type : Initialisation System call
*
* Description : This system calls is used to start the RTOS going such that it supervises the
* application processes.
*
* Arguments : prior Determines whether tasks ready to be executed are sorted prior to processing
* or not. If prior = 0 a FIFO queue function is implied, if prior = 1 the
* queue is sorted by task number in ascending order, as a higher priority is
* associated with smaller task number (task 0 would have the highest
* priority), such that the first task in the queue, which would eventually
* run, would be the one with the smallest task number having highest priority.
*
* Returns : None
*
********************************************************************************************************
*/
void RTOSGO(uchar prior)
{
if (prior == 1) /* Checks if tasks priorities */
Priority = 1; /* are to be enabled */
else
Priority = 0;
#if (TICK_TIMER == 0)
TH0 = BASIC_TICK / 256; /* Configure Timer 0 in 16-bit */
TL0 = BASIC_TICK % 256; /* timer mode for the 8051 */
TMOD &= 0xF0; /* Clear T0 mode control, leaving T1 untouched */
TMOD |= 0x01; /* Set T0 mode control */
TR0 = 1; /* Start timer 0 */
TF0 = 1; /* Cause first interrupt immediately */
#elif (TICK_TIMER == 1)
TH1 = BASIC_TICK / 256; /* Configure Timer 1 in 16-bit */
TL1 = BASIC_TICK % 256; /* timer mode for the 8051 */
TMOD &= 0x0F; /* Clear T1 mode control, leaving T0 untouched */
TMOD |= 0x10; /* Set T1 mode control */
TR1 = 1; /* Start timer 1 */
TF1 = 1; /* Cause first interrupt immediately */
#elif (TICK_TIMER == 2)
RCAP2H = BASIC_TICK / 256; /* Configures Timer 2 in 16-bit */
RCAP2L = BASIC_TICK % 256; /* auto-reload mode for the 8032 */
T2CON = 0x84; /* TR2 = TF2 = 1 */
#endif
TinQFlag = 1; /* Signals scheduler that tasks have been */
/* added to the queue. */
EA = 1; /* Interrupts are enabled, starting the RTOS */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : RUNNING_TASK_ID
*
* Function type : Inter-task Communication System call
*
* Description : This system call is used to check to get the number of the
* current task.
*
* Arguments : None
*
* Returns : Number of currently running task from which it must be called
*
********************************************************************************************************
*/
uchar RUNNING_TASK_ID(void)
{
return (Running);
}
/*
********************************************************************************************************
*
* Function name : SCHECK
*
* Function type : Inter-task Communication System call
*
* Description : This system call is used to check if the current task has its signal set. It tetsts
* whether there was any signal sent to it by some other task.
*
* Arguments : None
*
* Returns : 1 if its signal bit is set, 0 if not set
*
********************************************************************************************************
*/
uchar SCHECK(void)
{
EA = 0;
Check_Task_Max_SP();
if ((TaskFlags[Running] & SIGS) == SIGS) /* If a signal is */
{ /* present it’s cleared */
TaskFlags[Running] &= ~SIGS; /* and 1 is returned. */
EA = 1;
return 1;
}
else /* If a signal is not present, 0 is returned */
{
EA = 1;
return 0;
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : SIGNL
*
* Function type : Inter-task Communication System call
*
* Description : This system call is used to send a signal to another task.
*
* Arguments : task Represents the task to which a signal is required to be sent.
*
* Returns : None
*
********************************************************************************************************
*/
void SIGNL(uchar task)
{
EA = 0;
Check_Task_Max_SP();
if ((TaskFlags[task] & SIGW) == SIGW)
{
TaskFlags[task] &= ~SIGS; /* If a task has been waiting */
TaskFlags[task] &= ~SIGW; /* for a signal, the task no */
TTs[task] = NOT_TIMING; /* longer as to wait and is */
ReadyQTop++; /* added to the READY queue. */
*ReadyQTop = task;
TinQFlag = 1;
EA = 1;
}
else /* If it was not waiting, its */
TaskFlags[task] |= SIGS; /* signal sent bit is set */
EA = 1;
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : WAITS
*
* Function type : Event-Waiting System call
*
* Description : This system call causes a task to wait for a signal to arrive within a given number of
* RTOS ticks. If the signal is already present, the task continues to execute.
*
* Arguments : ticks Represents the number of ticks for which the task will wait for a signal to
* arrive. Valid range for this argument is 0 to 4294967295. A value of 0 means
* waiting forever for a signal to arrive.
*
* Returns : None
*
********************************************************************************************************
*/
void WAITS (ulong ticks)
{
EA = 0;
Check_Task_Max_SP();
if ((TaskFlags[Running] & SIGS) == SIGS) /* If signal already */
{ /* sent it clears the */
TaskFlags[Running] &= ~SIGS; /* signal and the task */
EA = 1; /* continues to run. */
}
else
{ /* If signal is not present */
TaskFlags[Running] |= SIGW; /* the task is sent in the */
TTs[Running] = ticks; /* waiting state, by causing */
QShift(); /* a task switch. */
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : WAITT
*
* Function type : Event-Waiting System call
*
* Description : This system call causes a task to go in the waiting state for a timeout period given
* by a defined number of RTOS ticks.
*
* Arguments : ticks Represents the number of ticks for which the task will wait. Valid range for
* this parameter is 1 to 4294967295. A zero waiting time parameter is set to 1
* by the RTOS itself, since a zero effectively kills the task, making it wait
* forever.
*
* Returns : None
*
********************************************************************************************************
*/
void WAITT (ulong ticks)
{
EA = 0;
Check_Task_Max_SP();
if (ticks == 0)
ticks = 1; /* Task’s timeout variable is updated */
TTs[Running] = ticks; /* and the task then enters the */
QShift(); /* waiting state. */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : WAITV
*
* Function type : Event-Waiting System call
*
* Description : This system call is used by a task to wait for the end of its periodic interval. If
* the interval has already passed, the task continues to execute.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void WAITV(void)
{
EA = 0;
Check_Task_Max_SP();
if ((TaskFlags[Running] & SIGV) == SIGV) /* If the periodic */
{ /* interval time has */
TaskFlags[Running] &= ~SIGV; /* has elapsed, the */
EA = 1; /* task continues to */
} /* execute. */
else
{ /* Else the task */
TaskFlags[Running] |= SIGV; /* enters the waiting */
QShift(); /* state. */
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : PERIODIC
*
* Function type : Event-Waiting System call
*
* Description : This system call causes a task to repeat its function every given number of RTOS ticks.
*
* Arguments : ticks Represents the length of the periodic interval in terms of RTOS ticks, after
* which the task repeats itself. Valid range for this parameter is 1 to
* 4294967295.
*
* Returns : None
*
********************************************************************************************************
*/
void PERIODIC (ulong ticks)
{
EA = 0;
Check_Task_Max_SP();
if (ticks == 0)
ticks = 1;
IntvalRld[Running] = ticks; /* Task’s periodic interval count */
IntvalCnt[Running] = ticks; /* and reload variables are */
EA = 1; /* initialised. */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : WAITI
*
* Function type : Event-Waiting System call
*
* Description : This system call causes a task to wait for a given event (interrupt). It identifies
* for which interrupt the task has to wait. Once identified – the task’s appropriate
* flag is set and the task is set in the waiting state by causing a task swap – the task
* would wait indefinitely for the interrupt as its timeout variable would be set to 0
* (NOT_TIMING) either during initialisation of the RTOS or after expiry of its timeout
* period due to other prior invocations of wait-inducing system calls.
*
* Arguments : intnum Represents the interrupt number associated with the given interrupt for
* which the calling task intends to wait
*
* Returns : None
*
********************************************************************************************************
*/
void WAITI(uchar intnum)
{
EA = 0;
Check_Task_Max_SP();
switch (intnum)
{
case 0: /* Interrupt number 0 */
TaskFlags[Running] |= EXT0W; /* Task made to wait for */
QShift(); /* external interrupt 0 */
break;
#if (TICK_TIMER != 0)
case 1: /* Interrupt number 1 */
TaskFlags[Running] |= TIM0W; /* Task made to wait for */
QShift(); /* timer 0 interrupt */
break;
#endif
case 2: /* Interrupt number 2 */
TaskFlags[Running] |= EXT1W; /* Task made to wait for */
QShift(); /* external interrupt 1 */
break;
#if (TICK_TIMER != 2)
case 3: /* Interrupt number 3 */
TaskFlags[Running] |= TIM1W; /* Task made to wait for */
QShift(); /* timer 1 interrupt */
break;
#endif
case 4: /* Interrupt number 4 */
TaskFlags[Running] |= SER0W; /* Task made to wait for */
QShift(); /* serial port interrupt */
break;
#if (TICK_TIMER != 2)
case 5: /* Interrupt number 3 */
TaskFlags[Running] |= TIM2W; /* Task made to wait for */
QShift(); /* timer 1 interrupt */
break;
#endif
default:
EA = 1;
break;
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : DEFER
*
* Function type : Task Suspention System call
*
* Description : This system call is used to stop the current task in order for the next task in the
* queue to execute. In the meantime the current task is placed at the end of the queue.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void DEFER(void)
{
EA = 0;
Check_Task_Max_SP();
TTs[Running] = NOT_TIMING;
ReadyQTop++; /* Task added to the end of the */
*ReadyQTop = Running; /* ready queue prior to causing */
QShift(); /* a task switch. */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : KILL
*
* Function type : Task Suspention System call
*
* Description : This system call kills the current task, by putting it permanently waiting, such that
* it never executes again. It also clears any set waiting signals which the task might
* have.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void KILL(void)
{
EA = 0;
Check_Task_Max_SP();
TaskFlags[Running] = 0; /* Task is killed by clearing it */
TTs[Running] = NOT_TIMING; /* flags, setting it to wait forver */
QShift(); /* and then cause a task switch. */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : QShift
*
* Function type : Context Switcher (Internal function)
*
* Description : This function is used to perform a context switch i.e. task swapping
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void QShift (void) using 1
{
uchar data i, temp;
uint data offset;
uchar idata * idata internal;
uchar data * idata qtask;
uchar data * idata pair;
Check_Task_Max_SP();
TinQFlag = 0;
SPTs[Running] = SP; /* Current task’s SP is saved */
internal = MAINSTACK;
offset = STACKSIZE * Running;
/* Current task’s stack is saved */
for (i = 0; i < STACKSIZE; i++)
Ext_Stack[offset + i] = *(internal + i);
qtask = ReadyQ; /* READY queue is shifted ahead */
while (qtask <= ReadyQTop) /* by one position */
{
*qtask = *(qtask + 1);
qtask++;
}
ReadyQTop–; /* Pointer to last task in queue is decremented */
if (ReadyQTop < ReadyQ) /* Ensure that this pointer is never */
ReadyQTop = ReadyQ; /* below the start of the READY queue */
if (Priority == 1) /* If task priorities are enabled */
{ /* the queue is sorted such that */
pair = ReadyQTop; /* the highest priority task */
while (pair > ReadyQ) /* becomes the running task, i.e. */
{ /* the one having the smallest */
/* task number. */
/* Just one scan through the list */
pair–;
if (*pair > *(pair + 1))
{
temp = *pair;
*pair = *(pair + 1);
*(pair + 1) = temp;
}
}
}
/* The first task in the READY queue */
Running = ReadyQ[0]; /* becomes the new running task */
offset = STACKSIZE * Running;
/* The new running task’s stack */
/* area is copied to internal RAM */
for (i = 0; i < STACKSIZE; i++)
*(internal + i) = Ext_Stack[offset + i];
SP = SPTs[Running]; /* The new running task’s SP is restored */
/* such that the new task will execute. */
EA = 1;
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_0
*
* Function type : Interrupt Service Routine
*
* Description : This is the external 0 interrupt ISR whose associated interrupt number is 0.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void Xtra_Int_0 (void) interrupt 0 using 1
{
EA = 0;
Check_Task_Max_SP();
Xtra_Int(EXT0W); /* Passes EXT0W for identification purposes */
}
/*
********************************************************************************************************
*
* Function name : RTOS_Timer_Int
*
* Function type : Scheduler Interrupt Service Routine
*
* Description : This is the RTOS scheduler ISR. It generates system ticks and calculates any remaining
* waiting and periodic interval time for each task.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
#if (TICK_TIMER == 0) /* If Timer 0 is used for the scheduler */
void RTOS_Timer_Int (void) interrupt 1 using 1
{
uchar data k; /* For the 8051, Timer 0 is used */
uchar data * idata q; /* for scheduling. */
bit data On_Q;
TH0 = BASIC_TICK / 256; /* Timer registers reloaded */
TL0 = BASIC_TICK % 256;
#elif (TICK_TIMER == 1) /* If Timer 1 is used for the scheduler */
void RTOS_Timer_Int (void) interrupt 3 using 1
{
uchar data k; /* For the 8051, Timer 0 is used */
uchar data * idata q; /* for scheduling. */
bit data On_Q;
TH1 = BASIC_TICK / 256; /* Timer registers reloaded */
TL1 = BASIC_TICK % 256;
#elif (TICK_TIMER == 2) /* If Timer 2 is used for the scheduler */
void RTOS_Timer_Int (void) interrupt 5 using 1
{
uchar data k; /* For the 8032, Timer 2 is used */
uchar data * idata q; /* for scheduling. */
bit data On_Q;
TF2 = 0; /* Timer 2 interrupt flag is cleared */
#endif
Check_Task_Max_SP();
for (k = 0; k < NOOFTASKS; k++)
{
if (IntvalCnt[k] != NOT_TIMING) /* Updates the tasks’ */
{ /* periodic intervals. */
IntvalCnt[k]–;
if (IntvalCnt[k] == NOT_TIMING)
{ IntvalCnt[k] = IntvalRld[k];
if ((TaskFlags[k] & SIGV) == SIGV)
{
/* If periodic interval */
/* has elapsed and the */ TaskFlags[k] &= ~SIGV;
/* task has been waiting */ q = ReadyQ;
/* for this to occur, the */ On_Q = 0;
/* task is placed in the */ while (q <= ReadyQTop)
/* READY queue, if it is */ {
/* verified that the task */ if (k == *q)
/* does not already reside */ {
/* in the queue, as now */ On_Q = 1;
/* the task no longer */ break;
/* requires to wait. */ }
q++;
}
if (On_Q == 0)
{
ReadyQTop++;
*ReadyQTop = k;
TinQFlag = 1;
}
}
/* If however the task */
/* was not waiting for */
/* this event, the task */
/* is not place in the */
/* the ready queue. */
else
TaskFlags[k] |= SIGV;
}
}
if (TTs[k] != NOT_TIMING)
{ /* Updates the tasks’ */
TTs[k]–; /* timeout variables. */
if (TTs[k] == NOT_TIMING)
{
ReadyQTop++; /* If a waiting task’s */
*ReadyQTop = k; /* timeout elapses */
TinQFlag = 1; /* the task is placed */
TaskFlags[k] &= ~SIGW; /* in the ready queue. */
}
}
/* If the idle task is running, when tasks are */
/* known to reside in the queue, a task switch */
/* is purposely induced so these tasks can run. */
}
if ((TinQFlag == 1) && (Running == IDLE_TASK))
QShift();
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_1
*
* Function type : Interrupt Service Routine
*
* Description : This is the Timer 0 ISR whose associated interrupt number is 1. It is only enabled in
* case an 8032 microcontroller is being used in combination with an EEPROM. The reason
* being is that without an EEPROM Timer 0 is not available on the 8032 and in case of
* the 8051 Timer 0 is already being used as the RTOS scheduler.
* It is also available if using the version 2 monitor ROM.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
#if (TICK_TIMER != 0)
/* Timer 0 interrupt used for RTOS on 8051 */
/* For the 8032, it can only be used with the modified monitor eprom or user eprom */
/* it is used for the single step in the old version monitor eprom */
void Xtra_Int_1 (void) interrupt 1 using 1
{
EA = 0;
Check_Task_Max_SP();
Xtra_Int(TIM0W); /* Passes TIM0W for identification purposes */
}
#endif
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_2
*
* Function type : Interrupt Service Routine
*
* Description : This is the external 1 interrupt ISR whose associated interrupt number is 2.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void Xtra_Int_2 (void) interrupt 2 using 1
{
EA = 0;
Check_Task_Max_SP();
Xtra_Int(EXT1W); /* Passes EXT1W for identification purposes */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_3
*
* Function type : Interrupt Service Routine
*
* Description : This is the Timer 1 ISR whose associated interrupt number is 3.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
#if (TICK_TIMER != 1)
void Xtra_Int_3 (void) interrupt 3 using 1
{
EA = 0;
Check_Task_Max_SP();
Xtra_Int(TIM1W); /* Passes TIM1W for identification purposes */
}
#endif
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_4
*
* Function type : Interrupt Service Routine
*
* Description : This is the serial port ISR whose associated interrupt number is 4.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
void Xtra_Int_4 (void) interrupt 4 using 1
{
EA = 0;
Check_Task_Max_SP();
Xtra_Int(SER0W); /* Passes SER0W for identification purposes */
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int_5
*
* Function type : Interrupt Service Routine
*
* Description : This is the Timer 2 ISR whose associated interrupt number is 5.
*
* Arguments : None
*
* Returns : None
*
********************************************************************************************************
*/
#if ( (CPU == 8032) && (TICK_TIMER != 2) )
void Xtra_Int_5 (void) interrupt 5 using 1
{
EA = 0;
TF2 = 0;
Check_Task_Max_SP();
Xtra_Int(TIM2W); /* Passes TIM2W for identification purposes */
}
#endif
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
*
* Function name : Xtra_Int
*
* Function type : Interrupt Handling (Internal function)
*
* Description : This function performs the operations required by the previous ISRs.
*
* Arguments : task_intflag Represents the flag mask for a given interrupt against which the
* byte storing the flags of each task will be compared in order to
* determine whether any task has been waiting for the interrupt in
* question.
*
* Returns : None
*
********************************************************************************************************
*/
void Xtra_Int (uchar task_intflag) using 1
{
uchar data k;
IntFlag = 0;
for (k = 0; k < NOOFTASKS; k ++)
{
if ((TaskFlags[k] & task_intflag) == task_intflag)
{
TaskFlags[k] &= ~task_intflag;
IntFlag = 1;
TTs[k] = NOT_TIMING; /* If it found that a task */
ReadyQTop++; /* has been waiting for the */
*ReadyQTop = k; /* given interrupt, it no */
} /* longer requires to wait */
} /* and is therefore placed */
/* on the READY queue. */
if ((IntFlag == 1) && (Running == IDLE_TASK))
{
TinQFlag = 1; /* If tasks are known to now reside in the */
QShift(); /* READY queue while the idle task is */
} /* running, a task switch is purposely */
/* induced, such that these tasks can run. */
else
{
if (IntFlag == 1) /* Otherwise, the ISR exits after */
TinQFlag = 1; /* interrupts are re-enabled. */
EA = 1;
}
}
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* RTOS KERNEL HEADER FILE
*
* For use with CRTOS_V2.c – Co-Operative RTOS written in C based on PaulOS by Ing. Paul P. Debono
* for use with the 8051 family of microcontrollers
*
* File : CRTOS_V2.h
* Revision : 7C
* Date : February 2005
* By : John Blaut, Paul P. Debono
*
* B. Eng. (Hons.) Elec. Course Final Year Project
* University Of Malta
*
********************************************************************************************************
*/
/*
********************************************************************************************************
* DATA TYPE DEFINITIONS
********************************************************************************************************
*/
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* RTOS USER DEFINITIONS
********************************************************************************************************
*/
#define CPU 8051 // set to 8051 or 8032
#define TICK_TIMER 0 // Set to 0, 1 or 2 to select which timer to use as the RTOS tick timer
#define TICKTIME 10 // Length of RTOS basic tick in msec – refer to the RTOS timing definitions
#define NOOFTASKS 8 // Number of tasks used in application
#define MAINSTACK 0x67 // Start address of stack area (see ?STACK *.M51 file)
#define STACKSIZE 0x0F // Number of bytes to allocate for the stack
#define STACK_CHECK 0 // Stack size checking flag
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* RTOS MACROS
********************************************************************************************************
*/
#define OS_CPU_IDLE() PCON |= 0x01 // Sets the microprocessor in idle mode
#define OS_CPU_DOWN() PCON |= 0x02 // Sets the microprocessor in power-down mode
#if STACK_CHECK // If stack size checking is enabled sets the current SP value to be the maximum SP
// for the current task in case it is higher than its present value
#define Check_Task_Max_SP(); MaxSPTs[Running] = (SP > MaxSPTs[Running]) ? SP : MaxSPTs[Running];
#else // Macro defined as nothing in order that it does nothing since stack size checking is disabled
#define Check_Task_Max_SP();
#endif
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* RTOS TIMING DEFINITIONS
********************************************************************************************************
*/
#define MSEC 900 // In theory 922 counts represent 1 msec assuming an
// 11.0592 MHz crystal. (Used 900 to compensate for
// overheads) One can experiment with other values
// depending on accuracy required.
#define TICKS_PER_SEC (1000 / TICKTIME) // Ensure that TICKTIME’s value is chosen such that this
#define TICKS_PER_MIN (60000 / TICKTIME) // quotient and hence all the following quotients result
#define TICKS_PER_HOUR (3600000 / TICKTIME) // in an integer. In theory, maximum value of TICKTIME
#define TICKS_PER_DAY (86400000 / TICKTIME) // is given by the value corresponding to CLOCK = 65535
#define CLOCK (TICKTIME * MSEC) // i.e. approx. 70-72 – However respecting the condition
#define BASIC_TICK (65535 – CLOCK + 1) // above, max. acceptable TICKTIME = 50 msecs. Hence all
// suitable values are: 1, 2, 4, 5, 8, 10, 20, 25, 40, 50
// For reliable time-dependent results a value of 10 or
// above is recommended depending upon the application
#define NOT_TIMING 0 // An indefinite period of waiting time in the RTOS is given by a value of 0
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* COMPILE-TIME ERROR TRAPPING
********************************************************************************************************
*/
#if (CPU != 8032) && (CPU != 8051)
#error Invalid CPU Setting
#endif
#if (NOOFTASKS > 254)
#error Number of tasks is out of range. The ReadyQ can store up to 254 tasks
#endif
#if (CPU == 8032)
#if ((MAINSTACK + STACKSIZE) > 0x100)
#error Internal RAM Space exceeded. Please recheck the MAINSTACK and STACKSIZE definitions
#endif
#elif (CPU == 8051)
#if ((MAINSTACK + STACKSIZE) > 0x80)
#error Internal RAM Space exceeded. Please recheck the MAINSTACK and STACKSIZE definitions
#endif
#endif
#if ((TICKTIME * 11059200 / 12000) > 65535)
#error Tick time value exceeds valid range of the timer counter setting
#endif
#if ((TICKTIME * 11059200 / 12000) < 65535) && ((1000 % TICKTIME) != 0)
#error Undesirable TICKTIME setting. Please choose from 1, 2, 4, 8, 10, 20, 25, 40, 50 ms
#endif
#if (CLOCK > 65535)
#error Timer counter setting exceeded valid range. Please recheck the TICKTIME and MSEC definitions
#endif
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* TASK-RELATED DEFINITIONS
********************************************************************************************************
*/
#define SIGS 0x80 // Signal-received flag mask
#define SIGW 0x40 // Waiting-for-singal flag mask
#define SIGV 0x20 // Periodic Interval flag
#define SER0W 0x10 // Serial interrupt flag mask (Interrupt number 4)
#define EXT1W 0x02 // External interrupt 1 flag mask (Interrupt number 2)
#define EXT0W 0x01 // External interrupt 0 flag mask (Interrupt number 0)
#if (TICK_TIMER == 0)
#define TIM1W 0x08 // Timer 1 interrupt flag mask (Interrupt number 3)
#define TIM2W 0x04 // Timer 2 interrupt flag mask (Interrupt number 5)
#elif (TICK_TIMER == 1)
#define TIM0W 0x08 // Timer 0 interrupt flag mask (Interrupt number 1)
#define TIM2W 0x04 // Timer 2 interrupt flag mask (Interrupt number 5)
#elif (TICK_TIMER == 2)
#define TIM0W 0x08 // Timer 0 interrupt flag mask (Interrupt number 1)
#define TIM1W 0x04 // Timer 1 interrupt flag mask (Interrupt number 3)
#endif
#define IDLE_TASK NOOFTASKS // Main endless loop in application given a task number equal to NOOFTASKS
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* ENHANCED EVENT-WAITING ADD-ON MACROS
********************************************************************************************************
*
* These macros perform the same functions of the WAITT, WAITS and PERIODIC calls but rather than ticks
* they accept absolute time values as parameters in terms of days, hours, minutes, seconds and millisecs
* This difference is denoted by the _A suffix – eg. WAITT_A() is the absolute-time version of WAITT()
*
* Range of values accepted:
*
* Using a minimum TICKTIME of 1 msec : 1 msecs – 49 days, 17 hours, 2 mins, 47 secs, 295 msecs
* Using a recommended TICKTIME of 10 msec : 10 msecs – 497 days, 2 hours, 27 mins, 52 secs, 950 msecs
* Using a maximum TICKTIME of 50 msec : 50 msecs – 2485 days, 12 hours, 19 mins, 24 secs, 750 msecs
* If the conversion from absolute time to ticks results in 0 (all parameters being 0 or overflow) this
* result is only accepted by WAITS() by virtue of how the WAITT(), WAITS() and PERIODIC() calls were
* written. In the case of the WAITT() and PERIODIC() calls the tick count would automatically be
* changed to 1 meaning an interval of eg. 50 msecs in case the TICKTIME is defined to be 50 msecs
*
* Liberal use of parentheses is made in the following macros in case the arguments might be expressions
*
********************************************************************************************************
*/
#define WAITT_A(D,H,M,S,ms) WAITT((ulong)((TICKS_PER_DAY*(##D)) + (TICKS_PER_HOUR*(##H)) + \
(TICKS_PER_MIN*(##M)) + (TICKS_PER_SEC*(##S)) + ((##ms)/TICKTIME)))
#define WAITS_A(D,H,M,S,ms) WAITS((ulong)((TICKS_PER_DAY*(##D)) + (TICKS_PER_HOUR*(##H)) + \
(TICKS_PER_MIN*(##M)) + (TICKS_PER_SEC*(##S)) + ((##ms)/TICKTIME)))
#define PERIODIC_A(D,H,M,S,ms) PERIODIC((ulong)((TICKS_PER_DAY*(##D)) + (TICKS_PER_HOUR*(##H)) + \
(TICKS_PER_MIN*(##M)) + (TICKS_PER_SEC*(##S)) + ((##ms)/TICKTIME)))
/*
********************************************************************************************************
*/
/*
********************************************************************************************************
* FUNCTION PROTOTYPES
********************************************************************************************************
*
*
* The following RTOS system calls do not receive any parameters :
* —————————————————————-
*/
void DEFER (void); // Stops current task and passes control to next task in queue
void KILL (void); // Kills a task – sets it waiting forever
uchar SCHECK (void); // Checks if running task’s signal bit is set
void WAITV (void); // Waits for end of task’s periodic interval
uchar RUNNING_TASK_ID(void); // Returns the number of the currently executing task
/*
* The following RTOS system calls do receive parameters :
* ——————————————————-
*/
void INIT_RTOS (uchar iemask); // Initialises all RTOS variables
void RTOSGO (uchar prior); // Starts the RTOS running with prioities if required
void SIGNL (uchar task); // Signals a task
void WAITI (uchar intnum); // Waits for an event (interrupt) to occur
void WAITT (ulong ticks); // Waits for a timeout period given by a defined number of ticks
void WAITS (ulong ticks); // Waits for a signal to arrive within a given number of ticks
void PERIODIC (ulong ticks); // Sets task to behave periodically every given number of ticks
void CREATE (uchar task, uint taskadd); // Creates a task
/*
* Other functions used internally by the RTOS :
* ———————————————
*/
void QShift (void); // Task swapping function
void RTOS_Timer_Int (void); // RTOS Scheduler ISR
void Xtra_Int (uchar task_intflag); // Function used by ISRs other than the RTOS Scheduler
void Xtra_Int_0 (void); // External Interrupt 0 ISR
#if (TICK_TIMER != 0 )
void Xtra_Int_1 (void); // Timer 0 ISR
#endif
void Xtra_Int_2 (void); // External Interrupt 1 ISR
#if (TICK_TIMER != 1 )
void Xtra_Int_3 (void); // Timer 1 ISR
#endif
void Xtra_Int_4 (void); // Serial Port ISR
#if (TICK_TIMER != 2 )
void Xtra_Int_5 (void); // Interrupt 5 (Timer 2) is not available on the 8051
#endif
/*
********************************************************************************************************
*/