The operating system I will describe, called TinyRealTime (TRT) was originally written by Dan Henriksson and Anton Cervin (technical report). (See also Resource−Constrained Embedded Control and Computing Systems,
Dan Henriksson, 2006). I extended it, by adding a realtime trace
ability, flexible soft timers, a mutex construct, a system dump and a
simple command shell. The context switch time is a few hundred cycles on
the 8-bit Atmel Mega1284, compiled using GCC. Full documentation is at http://people.ece.cornell.edu/land/courses/ece4760/TinyRealTime/index.html.
A realtime trace facility was added to TRT to enable following which
tasks are executing and which semaphores are signaled. There are also to
user defined events available. The trace facility uses one
port of the MCU to dump data to an oscilloscope. A simple 3-bit DAC is
used to convert task number and semaphore number to two separate
voltages for display. Each task number adds about 125 mV to the output,
so that when task 2, for example, is executing the voltage output is 250
mV. The semaphore number is specified when you initialize a semaphore.
The task number starts at zero for the null task, then each task has a
number defined by the order in which the tasks were created. Either of
the events may be used as a scope trigger or to display. The image shown
is from the realtime trace, which gives the task number
as a voltage on the top trace and the semaphore number on the bottom
trace.
With 4 tasks running, it takes about 700 cycles to perfrom a context switch by signalling a semaphore (Code
to test this). The actual time spent in the scheduler ISR (timer1
compare-match) is 390 cycles, plus 70 cycles to store state, plus 70
cycles to restore state equals 530 cycles to service a timer tick. The
scheduling scheme is Earliest Deadline First (EDF). Our student have
used the kernel for motor controllers, wireless pedometer (http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/f2013/esc73_jsw267/esc73_jsw267/esc...), ultrasonic nagigators (http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/f2013/xs46_ebl43/xs46_ebl43/xs46_eb...) and more.
The API supplied with TRT includes core real time routines:
void trtInitKernel(uint16_t idletask_stack) |
Sets up the kernel data structures. The parameter is the desired starck size of the idle task. For a null idle task, a stack size of 80 should be sufficient. |
void trtCreateTask(void (*fun)(void*), |
Identifies a function to the kernel as a thread. The parameters
specify a pointer to the function, the desired stack size, the initial release
time, the initial deadline time, and an abitrary data input
sturcture. The release time and deadline must be updated in each task
whenever trtSleepUntil is called. The task structures are
statically allocated. Be sure to configure MAXNBRTASKS in
the kernel file to be big enough. When created, each task initializes
35 bytes of storage for registers, but stacksize minimum is
around 40 bytes. If any task stack is too small, the system will crash! |
void trtTerminate(void) |
Terminates the running task. |
uint32_t trtCurrentTime(void) |
Get the current global time in timer ticks. |
void trtSleepUntil(uint32_t release, |
Puts a task to sleep by making it ineligible
to run until the release time. After the release time, it will run when
it has the nearest deadline. Never use this function in an ISR. The (deadline)
- (release time) should be greater than than the execution time
of the thread between trtSleepUntil calls so
that the kernel can meet all the deadlines. If you give a slow task a
release time equal to it's deadline, then it has to execute in zero
time to meet deadline, and nothing else can run until the slow task
completes. Experiment with this
test code by changing the difference in the spoiler task
while watching A.0... |
Like the dac use for monitoring the task running. Clever.