Usage
COPOS must be compiled with CodeVision.

COPOS reserves timer0 for internal timing, as well as registers r2-r13. This leaves registers r14-r21 for the user, since the compiler uses registers r0, r1, r22-R31.External ram is enabled, so Ports A and C and 2 pins on Port D are allocated to accessing the external ram, and can not be touched by the user. The user is free to use Port B and the rest of the pins on Port D. There is a system time (System_Time), that is updated every 2048 cycles (512 us) by the os. System_Time is a read only integer global variable that can be accessed by the user's jobs for a crude time estimate. Since it us updated every 2048 cycles, the maximum the user can time, with the system timer, is 2048*2^16 = ~34 seconds.

The user is allotted 1000 words of flash memory for their program. The user must define a User_Init(void) function that includes all of their initialization statements, as well as the jobs they want the operating system to begin with.

Jobs can be added to the queues one at a time, for each task, with the following functions.

unsigned char Schedule_Add_HP(void (*Task) (void), unsigned int Deadline, unsigned int Period, void (*After_Task) (void))

The user may specify where in the list of hard periodic jobs, they want the new job to be entered. This can be specified by passing in an After_Task. Adding soft and hard periodic jobs adds the jobs to the job list, but they will not be executed until the user calls Schedule_Compute, which creates tasks for each job, and places them in the queue (Queue_HP). It is important to enter soft and hard periodic tasks with factorable periods, since computing the schedule involves computing the least common multiple of these periods (which must be no greater than 16 bits). The smaller the lcd is between the job periods, the more elements the hp queue will contain. Since the length of the hp queue is finite, it is easily exceeded.

unsigned char Schedule_Add_SP(void (*Task) (void), unsigned int Period,void (*After_Task) (void))

unsigned char Schedule_Add_HA(void (*Task) (void), unsigned int Deadline)
unsigned char Schedule_Add_SA(void (*Task) (void), unsigned int Deadline)

Adding soft and hard periodic jobs causes COPOS to add their task directly to the soft and hard aperiodic queues. Thus, these jobs can be executed the next time the os looks into their queues for elements.

The user can call Schedule_Compute to compute the hard/soft periodic schedule. Soft periodic jobs are scheduled with Hard Periodics. However, they differ in that executing a soft periodic task from the hard periodic queue just means moving the soft periodic task to the soft periodic queue until it is ready to be executed (there is no hard periodic task that needs the execution time). The user must always call Schedule_Compute after adding a hard or soft periodic task, if they want it to start executing.

Unsigned char Schedule_Compute(void)

Jobs can be deleted from the queues one at a time, for each task, with the following functions.

unsigned char Schedule_Delete_HP(void (*Task) (void))
unsigned char Schedule_Delete_HA(void (*Task) (void))
unsigned char Schedule_Delete_SP(void (*Task) (void))
unsigned char Schedule_Delete_SA(void (*Task) (void))

Each job (function) written by the user must exit as follows.

Call_OS;
Job_Return();
Return_OS;

Call_OS turns off interrupts and Return_OS turns interrupts back on. Job_Return calls upon the os to determine and prepare to execute the next task. If no task is scheduled to run next (in the hard periodic queue), and there are no other elements in any of the other queues, the function NO_TASK (an infinite while loop), will be called until there exists another task to execute.

The user can make use of locks in the following fashion. The user can use as many different locks as they want. Each lock is identified by a unique number (between 0 and the number of locks that are used-1). The Number parameter in the following calls refers to which lock you are calling. Locks are a great way to implement task synchronization. For example, locks can prevent two different user tasks from writing to a global variable at the same time.

void Lock(unsigned char Number);
void Unlock(unsigned char Number);

The user must insert the lock call before the critical section and the unlock call directly after. Deadlock can be caused if the user is not careful about where they place their locks. Deadlock occurs when two user tasks are waiting for the other to unlock, and thus, they can never finish.

It is almost inevitable that a good portion of the operating system's time will be spent executing NO_TASK, for it is not often that all of the cpu time is taken up with tasks.